//
//  WithdrawalAuditViewModelTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/25.
//

import XCTest
@testable import suixinkan

@MainActor
/// 提现审核测试，覆盖分页、筛选和失败清理。
final class WithdrawalAuditViewModelTests: XCTestCase {
    /// 测试首次加载、筛选统计和分页加载。
    func testLoadFilterAndLoadMore() async throws {
        let api = WithdrawalAuditWalletMock()
        api.withdrawResponses = [
            WalletWithdrawListResponse(total: 3, item: [
                try record(id: 1, status: "审核中"),
                try record(id: 2, status: "已到账")
            ]),
            WalletWithdrawListResponse(total: 3, item: [
                try record(id: 3, status: "待打款")
            ])
        ]
        let viewModel = WithdrawalAuditViewModel()

        await viewModel.reload(api: api)

        XCTAssertEqual(api.withdrawPages, [1])
        XCTAssertEqual(viewModel.totalCount, 3)
        XCTAssertEqual(viewModel.processingCount, 1)
        XCTAssertEqual(viewModel.completedCount, 1)
        XCTAssertTrue(viewModel.hasMore)

        viewModel.selectFilter(.processing)
        XCTAssertEqual(viewModel.filteredRecords.map(\.id), [1])

        await viewModel.loadMore(api: api)

        XCTAssertEqual(api.withdrawPages, [1, 2])
        XCTAssertEqual(viewModel.records.map(\.id), [1, 2, 3])
        XCTAssertEqual(viewModel.processingCount, 2)
        XCTAssertFalse(viewModel.hasMore)
    }

    /// 测试刷新失败会清空旧记录和分页状态。
    func testReloadFailureClearsStaleRecordsAndPaging() async throws {
        let api = WithdrawalAuditWalletMock()
        api.withdrawResponses = [
            WalletWithdrawListResponse(total: 2, item: [try record(id: 1, status: "审核中")])
        ]
        let viewModel = WithdrawalAuditViewModel()

        await viewModel.reload(api: api)
        XCTAssertEqual(viewModel.records.map(\.id), [1])
        XCTAssertTrue(viewModel.hasMore)

        api.withdrawError = TestError.sample
        await viewModel.reload(api: api)

        XCTAssertTrue(viewModel.records.isEmpty)
        XCTAssertFalse(viewModel.hasMore)
        XCTAssertTrue(viewModel.loadFailed)
        XCTAssertEqual(viewModel.loadFailureReason, TestError.sample.localizedDescription)
    }

    /// 测试空列表和尾页边界不会继续加载。
    func testEmptyListAndLastPageDoNotLoadMore() async {
        let api = WithdrawalAuditWalletMock()
        api.withdrawResponses = [WalletWithdrawListResponse(total: 0, item: [])]
        let viewModel = WithdrawalAuditViewModel()

        await viewModel.reload(api: api)
        await viewModel.loadMore(api: api)

        XCTAssertTrue(viewModel.records.isEmpty)
        XCTAssertEqual(api.withdrawPages, [1])
        XCTAssertFalse(viewModel.hasMore)
    }

    private func record(id: Int64, amount: String = "10", status: String) throws -> WalletWithdrawRecord {
        let data = """
        {"id":\(id),"amount":"\(amount)","created_at":"2026-06-25 10:00","status_label":"\(status)","expected_at":"2026-06-26","audit_time":"2026-06-25 11:00","completed_at":""}
        """.data(using: .utf8)!
        return try JSONDecoder().decode(WalletWithdrawRecord.self, from: data)
    }
}

@MainActor
private final class WithdrawalAuditWalletMock: WalletServing {
    var withdrawResponses: [WalletWithdrawListResponse] = []
    var withdrawError: Error?
    private(set) var withdrawPages: [Int] = []

    func walletSummary(type: Int) async throws -> WalletSummaryResponse { WalletSummaryResponse() }
    func walletEarningDetail(startDate: String, endDate: String, page: Int, pageSize: Int) async throws -> WalletEarningDetailResponse { WalletEarningDetailResponse() }
    func walletWithdrawList(page: Int, pageSize: Int) async throws -> WalletWithdrawListResponse {
        withdrawPages.append(page)
        if let withdrawError { throw withdrawError }
        return withdrawResponses.isEmpty ? WalletWithdrawListResponse() : withdrawResponses.removeFirst()
    }
    func withdrawInfo() async throws -> WithdrawInfoResponse {
        let data = #"{"amount_withdrawable":"0","min_withdraw_amount":"1","max_single_withdraw_amount":"100","max_daily_withdraw_amount":"100","user_phone":"13800000000","bank_card":{},"withdraw_info":[]}"#.data(using: .utf8)!
        return try JSONDecoder().decode(WithdrawInfoResponse.self, from: data)
    }
    func withdrawSendSms() async throws {}
    func withdrawApply(amount: String, smsCode: String) async throws {}
    func bankCardInfo() async throws -> BankCardInfoResponse { BankCardInfoResponse(bankCard: nil) }
    func bankList() async throws -> BankListResponse { BankListResponse() }
    func areas() async throws -> [AreaNode] { [] }
    func bankCardVerifyCode() async throws {}
    func updateBankInfo(_ request: UpdateBankInfoRequest) async throws {}
    func pointOverview(staffId: Int) async throws -> PointOverviewResponse { PointOverviewResponse() }
    func pointWithdrawApply(points: Int, remark: String) async throws {}
    func pointWithdrawList(status: Int?, page: Int, pageSize: Int) async throws -> PointWithdrawListResponse { PointWithdrawListResponse() }
}

private enum TestError: LocalizedError {
    case sample

    var errorDescription: String? {
        "测试错误"
    }
}
