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

import XCTest
@testable import suixinkan

@MainActor
/// 景区结算测试，覆盖结算申请和审核记录聚合。
final class ScenicSettlementViewModelTests: XCTestCase {
    /// 测试已有景区会从可申请结算景区中排除。
    func testSettlementLoadExcludesExistingScenics() async {
        let permissionAPI = ScenicSettlementPermissionMock()
        permissionAPI.scenicList = ScenicListAllResponse(total: 3, list: [
            ScenicListItem(id: 1, name: "已有景区"),
            ScenicListItem(id: 2, name: "新增景区"),
            ScenicListItem(id: 3, name: "另一个景区")
        ])
        let viewModel = ScenicSettlementViewModel()

        await viewModel.load(
            api: permissionAPI,
            existingScenics: [BusinessScope(id: 1, name: "已有景区", kind: .scenic)]
        )

        XCTAssertEqual(viewModel.options.map(\.id), [2, 3])
    }

    /// 测试金额校验覆盖空值、0、负数、超过两位小数和有效金额。
    func testSettlementAmountValidation() {
        let viewModel = ScenicSettlementViewModel()

        XCTAssertEqual(viewModel.amountValidation, .empty)

        viewModel.amountText = "0"
        XCTAssertEqual(viewModel.amountValidation, .notPositive)

        viewModel.amountText = "-1"
        XCTAssertEqual(viewModel.amountValidation, .invalidFormat)

        viewModel.amountText = "1.234"
        XCTAssertEqual(viewModel.amountValidation, .invalidFormat)

        viewModel.amountText = "12.30"
        XCTAssertEqual(viewModel.amountValidation, .valid)
    }

    /// 测试多景区提交 body 正确且成功后清空表单。
    func testSettlementSubmitMultipleScenicsAndClearsForm() async {
        let submitAPI = ScenicSettlementSubmitMock()
        let viewModel = ScenicSettlementViewModel()
        viewModel.options = [
            ScenicSettlementOption(id: 3, name: "三号", selected: false),
            ScenicSettlementOption(id: 2, name: "二号", selected: false)
        ]
        viewModel.toggleScenic(id: 3)
        viewModel.toggleScenic(id: 2)
        viewModel.amountText = "99"
        viewModel.remarkText = "  活动结算  "

        let success = await viewModel.submit(api: submitAPI)

        XCTAssertTrue(success)
        XCTAssertEqual(submitAPI.requests, [
            ScenicSettlementSubmitRequest(scenicId: 2, applyAmount: "99.00", applyRemark: "活动结算"),
            ScenicSettlementSubmitRequest(scenicId: 3, applyAmount: "99.00", applyRemark: "活动结算")
        ])
        XCTAssertTrue(viewModel.selectedScenicIds.isEmpty)
        XCTAssertEqual(viewModel.amountText, "")
        XCTAssertEqual(viewModel.remarkText, "")
        XCTAssertEqual(viewModel.message, "提交成功，等待审核")
    }

    /// 测试任一提交失败会保留当前表单并透出错误。
    func testSettlementSubmitFailureKeepsForm() async {
        let submitAPI = ScenicSettlementSubmitMock()
        submitAPI.errorAfterRequestCount = 1
        let viewModel = ScenicSettlementViewModel()
        viewModel.options = [
            ScenicSettlementOption(id: 1, name: "一号", selected: false),
            ScenicSettlementOption(id: 2, name: "二号", selected: false)
        ]
        viewModel.toggleScenic(id: 1)
        viewModel.toggleScenic(id: 2)
        viewModel.amountText = "10"

        let success = await viewModel.submit(api: submitAPI)

        XCTAssertFalse(success)
        XCTAssertEqual(viewModel.selectedScenicIds, [1, 2])
        XCTAssertEqual(viewModel.amountText, "10")
        XCTAssertEqual(viewModel.message, TestError.sample.localizedDescription)
    }

    /// 测试审核记录两个通道都成功时汇总正确。
    func testReviewLoadSuccessAggregatesRecords() async {
        let permissionAPI = ScenicSettlementPermissionMock()
        permissionAPI.scenicPendings = ScenicApplicationPendingsResponse(items: [
            ScenicApplicationPendingResponse(id: 1, scenicName: "景区", status: 1)
        ])
        permissionAPI.roleApplies = [
            RoleApplyPendingResponse(id: 2, roleId: 1, roleName: "摄影师", status: 2)
        ]
        let viewModel = ScenicSettlementReviewViewModel()

        await viewModel.load(api: permissionAPI)

        XCTAssertEqual(viewModel.scenicApplications.count, 1)
        XCTAssertEqual(viewModel.roleApplications.count, 1)
        XCTAssertEqual(viewModel.pendingCount, 1)
        XCTAssertFalse(viewModel.loadFailedAll)
    }

    /// 测试单通道失败时保留另一通道数据。
    func testReviewSingleChannelFailureKeepsOtherChannel() async {
        let permissionAPI = ScenicSettlementPermissionMock()
        permissionAPI.scenicPendingsError = TestError.sample
        permissionAPI.roleApplies = [
            RoleApplyPendingResponse(id: 2, roleId: 1, roleName: "摄影师", status: 1)
        ]
        let viewModel = ScenicSettlementReviewViewModel()

        await viewModel.load(api: permissionAPI)

        XCTAssertTrue(viewModel.scenicLoadFailed)
        XCTAssertFalse(viewModel.roleLoadFailed)
        XCTAssertFalse(viewModel.loadFailedAll)
        XCTAssertEqual(viewModel.roleApplications.count, 1)
    }

    /// 测试双通道失败时进入整页失败态。
    func testReviewBothChannelsFailureMarksFullFailure() async {
        let permissionAPI = ScenicSettlementPermissionMock()
        permissionAPI.scenicPendingsError = TestError.sample
        permissionAPI.roleAppliesError = TestError.sample
        let viewModel = ScenicSettlementReviewViewModel()

        await viewModel.load(api: permissionAPI)

        XCTAssertTrue(viewModel.loadFailedAll)
        XCTAssertTrue(viewModel.scenicApplications.isEmpty)
        XCTAssertTrue(viewModel.roleApplications.isEmpty)
    }

    /// 测试审核状态文案和语义映射。
    func testReviewStatusMapping() {
        let viewModel = ScenicSettlementReviewViewModel()

        XCTAssertEqual(viewModel.statusText(1), "待审核")
        XCTAssertEqual(viewModel.statusText(2), "已通过")
        XCTAssertEqual(viewModel.statusText(3), "已驳回")
        XCTAssertEqual(viewModel.statusText(9), "已取消")
        XCTAssertEqual(viewModel.statusText(99), "未知")
        XCTAssertEqual(viewModel.statusIcon(2), "checkmark.circle.fill")
    }
}

@MainActor
private final class ScenicSettlementPermissionMock: ScenicPermissionServing {
    var scenicList = ScenicListAllResponse(total: 0, list: [])
    var scenicListError: Error?
    var scenicPendings = ScenicApplicationPendingsResponse()
    var scenicPendingsError: Error?
    var roleApplies: [RoleApplyPendingResponse] = []
    var roleAppliesError: Error?

    func scenicListAll() async throws -> ScenicListAllResponse {
        if let scenicListError { throw scenicListError }
        return scenicList
    }

    func areas() async throws -> [ScenicAreaNode] { [] }

    func scenicApplicationPendingAll() async throws -> ScenicApplicationPendingsResponse {
        if let scenicPendingsError { throw scenicPendingsError }
        return scenicPendings
    }

    func scenicSubmit(_ request: ScenicApplicationSubmitRequest) async throws {}
    func scenicApplicationUploadPlaceholder(_ items: [ScenicApplicationUploadPlaceholder]) async throws {}

    func roleApplyAll() async throws -> [RoleApplyPendingResponse] {
        if let roleAppliesError { throw roleAppliesError }
        return roleApplies
    }

    func roleApplySubmit(roleId: Int, scenicIds: [Int]) async throws {}
}

@MainActor
private final class ScenicSettlementSubmitMock: ScenicSettlementServing {
    var requests: [ScenicSettlementSubmitRequest] = []
    var errorAfterRequestCount: Int?

    func scenicSettlementSubmit(_ request: ScenicSettlementSubmitRequest) async throws {
        requests.append(request)
        if let errorAfterRequestCount, requests.count >= errorAfterRequestCount {
            throw TestError.sample
        }
    }
}

private enum TestError: LocalizedError {
    case sample

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