//
//  InviteTests.swift
//  suixinkanTests
//

import XCTest
@testable import suixinkan

/// 邀请 API 和 ViewModel 测试。
@MainActor
final class InviteTests: XCTestCase {
    /// 测试邀请 API path 和分页参数。
    func testInviteAPIUsesExpectedRequests() async throws {
        let session = MockURLSession(responses: [Self.inviteInfoEnvelope, Self.inviteUsersEnvelope])
        let api = InviteAPI(client: APIClient(environment: .testing, session: session))

        let info = try await api.inviteInfo()
        let users = try await api.inviteUserList(page: 0, pageSize: 0)

        XCTAssertEqual(info.inviteCode, "ABC123")
        XCTAssertEqual(users.first?.realName, "摄影师")
        XCTAssertEqual(session.requests.map { $0.url?.path }, [
            "/api/yf-handset-app/photog/invite-info",
            "/api/yf-handset-app/photog/invite/user-list",
        ])
        let query = queryItems(from: session.requests[1])
        XCTAssertEqual(query["page"], "1")
        XCTAssertEqual(query["page_size"], "1")
    }

    /// 测试邀请信息模型宽松解码。
    func testInviteInfoResponseDecodesLossyFields() throws {
        let json = Data(#"{"enable_invite":"1","invite_code":123,"invite_url":"https://invite.example.com","description":["规则1"]}"#.utf8)
        let response = try JSONDecoder().decode(InviteInfoResponse.self, from: json)

        XCTAssertTrue(response.enableInvite)
        XCTAssertEqual(response.inviteCode, "123")
        XCTAssertEqual(response.description, ["规则1"])
    }

    /// 测试邀请页加载信息并生成二维码。
    func testPhotographerInviteViewModelLoadsInfoAndGeneratesQRCode() async {
        let viewModel = PhotographerInviteViewModel()

        await viewModel.reload(api: FakeInviteService())

        XCTAssertEqual(viewModel.inviteCode, "ABC123")
        XCTAssertEqual(viewModel.inviteUrl, "https://invite.example.com")
        XCTAssertEqual(viewModel.rules, ["规则1"])
        XCTAssertNotNil(viewModel.qrImage)
        XCTAssertFalse(viewModel.isLoading)
    }

    /// 测试邀请记录首次加载邀请用户和钱包汇总。
    func testInviteRecordLoadsInviteRowsAndSummary() async {
        let inviteAPI = FakeInviteService()
        let walletAPI = FakeWalletService()
        let viewModel = InviteRecordViewModel()

        await viewModel.loadInitial(inviteAPI: inviteAPI, walletAPI: walletAPI)

        XCTAssertEqual(viewModel.inviteRows.first?.title, "摄影师")
        XCTAssertEqual(viewModel.totalRewardText, "100.00")
        XCTAssertEqual(viewModel.withdrawableText, "50.00")
        XCTAssertEqual(walletAPI.summaryTypes, [2])
        XCTAssertFalse(viewModel.canLoadMoreInvite)
    }

    /// 测试奖励 Tab 加载钱包流水并使用 type=2。
    func testInviteRecordRewardTabLoadsWalletTransactions() async {
        let inviteAPI = FakeInviteService()
        let walletAPI = FakeWalletService()
        let viewModel = InviteRecordViewModel()

        await viewModel.selectTab(.reward, inviteAPI: inviteAPI, walletAPI: walletAPI)

        XCTAssertEqual(viewModel.selectedTab, .reward)
        XCTAssertEqual(viewModel.rewardRows.first?.amount, "8.00")
        XCTAssertEqual(walletAPI.transactionQueries.first?.type, 2)
        XCTAssertEqual(walletAPI.transactionQueries.first?.page, 1)
    }

    /// 测试首页菜单 catalog 包含邀请相关 URI。
    func testHomeMenuCatalogContainsInviteRoutes() {
        XCTAssertEqual(HomeMenuCatalog.item(for: "registration_invitation")?.title, "注册邀请")
        XCTAssertEqual(HomeMenuCatalog.item(for: "photographer_invite")?.title, "邀请摄影师")
        XCTAssertEqual(HomeMenuCatalog.item(for: "invite_record")?.title, "邀请记录")
    }

    private static let inviteInfoEnvelope = Data(#"{"code":100000,"msg":"success","data":{"enable_invite":"1","invite_code":"ABC123","invite_url":"https://invite.example.com","description":["规则1"]}}"#.utf8)
    private static let inviteUsersEnvelope = Data(#"{"code":100000,"msg":"success","data":[{"id":"1","real_name":"摄影师","phone":"13800000000","avatar":"","created_at":"2026-06-24","invite_level":"1"}]}"#.utf8)
}

@MainActor
/// 邀请服务测试替身。
private final class FakeInviteService: InviteServing {
    func inviteInfo() async throws -> InviteInfoResponse {
        InviteInfoResponse(
            enableInvite: true,
            inviteCode: "ABC123",
            inviteUrl: "https://invite.example.com",
            description: ["规则1"]
        )
    }

    func inviteUserList(page: Int, pageSize: Int) async throws -> [InviteUserItem] {
        [
            InviteUserItem(
                id: 1,
                realName: "摄影师",
                phone: "13800000000",
                avatar: "",
                createdAt: "2026-06-24",
                inviteLevel: 1
            ),
        ]
    }
}

@MainActor
/// 钱包服务测试替身。
private final class FakeWalletService: WalletServing {
    private(set) var summaryTypes: [Int] = []
    private(set) var transactionQueries: [(page: Int, pageSize: Int, type: Int)] = []

    func walletSummary(type: Int) async throws -> WalletSummaryResponse {
        summaryTypes.append(type)
        return WalletSummaryResponse(amountTotal: "100.00", amountCurrentBalance: "20.00", amountWithdrawable: "50.00")
    }

    func walletTransactionList(
        startDate: String,
        endDate: String,
        page: Int,
        pageSize: Int,
        type: Int
    ) async throws -> WalletTransactionListResponse {
        transactionQueries.append((page, pageSize, type))
        return WalletTransactionListResponse(
            total: 1,
            list: [
                WalletTransactionItem(
                    id: 1,
                    amount: "8.00",
                    transactionType: 2,
                    transactionTypeLabel: "邀请奖励",
                    createdAt: "2026-06-24",
                    withdrawLabel: "可提现"
                ),
            ]
        )
    }
}

private func queryItems(from request: URLRequest) -> [String: String] {
    URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?
        .queryItems?
        .reduce(into: [String: String]()) { result, item in
            result[item.name] = item.value
        } ?? [:]
}
