//
//  PaymentCollectionDetailsViewModelTests.swift
//  suixinkanTests
//

import XCTest
@testable import suixinkan

/// 收款详情 ViewModel 测试。
@MainActor
final class PaymentCollectionDetailsViewModelTests: XCTestCase {

    private var appStore: AppStore!

    override func setUp() {
        let defaults = UserDefaults(suiteName: "PaymentCollectionDetailsViewModelTests")!
        defaults.removePersistentDomain(forName: "PaymentCollectionDetailsViewModelTests")
        appStore = AppStore(defaults: defaults)
        appStore.session.configureTestAccount(userId: "10001", scenicId: 10)
        appStore.session.currentScenicName = "测试景区"
    }

    override func tearDown() {
        appStore.logout()
        super.tearDown()
    }

    func testPaymentAmountValidatorRejectsEmptyAmount() {
        XCTAssertEqual(PaymentAmountValidator.validationMessage(for: ""), "请输入收款金额")
    }

    func testPaymentAmountValidatorRejectsZeroAmount() {
        XCTAssertEqual(PaymentAmountValidator.validationMessage(for: "0"), "请输入有效的收款金额")
    }

    func testPaymentAmountValidatorRejectsMoreThanTwoDecimals() {
        XCTAssertEqual(PaymentAmountValidator.validationMessage(for: "1.234"), "金额最多支持两位小数")
    }

    func testPaymentAmountValidatorAcceptsValidAmount() {
        XCTAssertNil(PaymentAmountValidator.validationMessage(for: "12.50"))
    }

    func testShowSetAmountDialogRequiresPayCode() {
        let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)
        var message: String?
        viewModel.onShowMessage = { message = $0 }

        viewModel.showSetAmountDialog()

        XCTAssertEqual(message, "请先获取收款码")
        XCTAssertFalse(viewModel.showAmountDialog)
    }

    func testConfirmAmountBuildsDynamicURL() async throws {
        let payCodeJSON = """
        {"code":100000,"msg":"success","data":{"static_pay_url":"https://pay.example.com/static","dynamic_pay_url":"https://pay.example.com/dynamic"}}
        """.data(using: .utf8)!
        let session = MockURLSession(responses: [payCodeJSON])
        let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)

        await viewModel.loadPayCode(api: api)
        viewModel.updateAmount("88.50")
        viewModel.updateRemark("测试备注")
        viewModel.confirmAmount()

        XCTAssertTrue(viewModel.hasPayCode)
        XCTAssertFalse(viewModel.showAmountDialog)
    }

    func testToggleReceiveVoicePersistsInAppStore() {
        let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)
        viewModel.refreshLocalState()

        XCTAssertFalse(viewModel.isVoiceBroadcastOpen)
        viewModel.toggleReceiveVoice()
        XCTAssertTrue(appStore.payment.isOpenReceiveVoice)
        XCTAssertTrue(viewModel.isVoiceBroadcastOpen)
    }

    func testNalatiAccountInfoUsesLoggedInPhotographerAndAllPermissionStores() {
        appStore.session.userName = "摄影师小王"
        appStore.permissions.saveRolePermissionList([
            RolePermissionResponse(
                role: RoleInfo(id: 41, name: "摄影师", roleCode: "photographer"),
                store: [
                    RolePermissionStoreInfo(id: 1, name: "空中草原店"),
                    RolePermissionStoreInfo(id: 2, name: "河谷草原店"),
                ]
            ),
        ])

        let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)

        XCTAssertEqual(viewModel.displayPhotographerName, "摄影师小王")
        XCTAssertEqual(viewModel.displayStoreNames, "空中草原店、河谷草原店")
    }

    func testLoadPayCodeClearsQRWhenScenicMissing() async {
        appStore.session.currentScenicId = 0
        let session = MockURLSession(responses: [])
        let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)

        await viewModel.loadPayCode(api: api)

        XCTAssertFalse(viewModel.hasPayCode)
        XCTAssertEqual(session.requests.count, 0)
    }

    func testNalatiLoadsCachedConfigBeforeRefreshingRemoteConfig() async throws {
        let defaults = UserDefaults(suiteName: "PaymentCollectionDetailsViewModelTests.Config")!
        defaults.removePersistentDomain(forName: "PaymentCollectionDetailsViewModelTests.Config")
        let cache = PayPageConfigCache(defaults: defaults, environment: .testing)
        let cached = PayPageConfig(
            background: "https://cache.example.com/background.png",
            logo: "https://cache.example.com/logo.png",
            title: "缓存标题",
            subtitle: "缓存副标题"
        )
        cache.save(cached, scenicId: 128)
        appStore.session.currentScenicId = 128
        appStore.session.currentScenicName = "那拉提景区"

        let responseJSON = """
        {"code":100000,"msg":"success","data":{
          "background":"https://new.example.com/background.png",
          "logo":"",
          "title":"最新标题",
          "subtitle":""
        }}
        """.data(using: .utf8)!
        let session = MockURLSession(responses: [responseJSON])
        let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = PaymentCollectionDetailsViewModel(
            appStore: appStore,
            payPageConfigCache: cache
        )

        XCTAssertEqual(viewModel.payPageConfig, cached)
        await viewModel.loadPayPageConfig(api: api)

        XCTAssertEqual(viewModel.payPageConfig.background, "https://new.example.com/background.png")
        XCTAssertEqual(viewModel.payPageConfig.logo, cached.logo)
        XCTAssertEqual(viewModel.payPageConfig.title, "最新标题")
        XCTAssertEqual(viewModel.payPageConfig.subtitle, cached.subtitle)
        XCTAssertEqual(viewModel.brandingRefreshVersion, 1)
        XCTAssertEqual(session.requests.first?.url?.path, "/api/yf-handset-app/photog/pay-page-config")
        XCTAssertEqual(
            URLComponents(url: try XCTUnwrap(session.requests.first?.url), resolvingAgainstBaseURL: false)?
                .queryItems?
                .first(where: { $0.name == "scenic_id" })?
                .value,
            "128"
        )
    }

    func testNonNalatiDoesNotRequestPayPageConfig() async {
        let session = MockURLSession(responses: [])
        let api = PaymentAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = PaymentCollectionDetailsViewModel(appStore: appStore)

        await viewModel.loadPayPageConfig(api: api)

        XCTAssertFalse(viewModel.usesNalatiBranding)
        XCTAssertEqual(session.requests.count, 0)
    }

    func testPayPageConfigCacheIsEnvironmentScoped() {
        let defaults = UserDefaults(suiteName: "PaymentCollectionDetailsViewModelTests.Environment")!
        defaults.removePersistentDomain(forName: "PaymentCollectionDetailsViewModelTests.Environment")
        let production = PayPageConfigCache(defaults: defaults, environment: .production)
        let testing = PayPageConfigCache(defaults: defaults, environment: .testing)

        production.save(.nalatiDefault, scenicId: 128)

        XCTAssertEqual(production.config(scenicId: 128), .nalatiDefault)
        XCTAssertNil(testing.config(scenicId: 128))
    }
}
