//
//  PermissionApplyViewModelTests.swift
//  suixinkanTests
//

import XCTest
@testable import suixinkan

@MainActor
/// 权限申请 ViewModel 测试。
final class PermissionApplyViewModelTests: XCTestCase {

    private var appStore: AppStore!
    private var defaults: UserDefaults!

    override func setUp() {
        defaults = UserDefaults(suiteName: "PermissionApplyViewModelTests")!
        defaults.removePersistentDomain(forName: "PermissionApplyViewModelTests")
        appStore = AppStore(defaults: defaults)
        appStore.session.configureTestAccount()
        appStore.permissions.saveRolePermissionList([
            RolePermissionResponse(
                role: RoleInfo(id: 10, name: "摄影师", roleCode: "PHOTOGRAPHER", notes: "拍摄"),
                scenic: [ScenicInfo(id: 100, name: "已有景区", status: 1)]
            ),
        ])
    }

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

    func testExistingScenicIsDisabledAfterLoad() async throws {
        let scenicJSON = """
        {"code":100000,"msg":"success","data":{"total":2,"list":[{"id":100,"name":"已有景区"},{"id":101,"name":"新景区"}]}}
        """.data(using: .utf8)!
        let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [scenicJSON])))
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "拍摄", isSelected: true))

        await viewModel.loadAvailableScenics(api: api)

        let existing = viewModel.availableScenics.first { $0.id == 100 }
        let fresh = viewModel.availableScenics.first { $0.id == 101 }
        XCTAssertEqual(existing?.isDisabled, true)
        XCTAssertEqual(existing?.isSelected, true)
        XCTAssertEqual(fresh?.isDisabled, false)
        if let existing { viewModel.toggleScenic(existing) }
        XCTAssertTrue(viewModel.selectedScenicIds.isEmpty)
    }

    func testSubmitValidationRequiresRoleAndScenic() async {
        let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [])))
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        var message: String?
        viewModel.onShowMessage = { message = $0 }

        await viewModel.submit(api: api)
        XCTAssertEqual(message, "请选择景区和角色")
        XCTAssertFalse(viewModel.canSubmit)

        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "", isSelected: true))
        await viewModel.submit(api: api)
        XCTAssertEqual(message, "请选择景区和角色")
        XCTAssertFalse(viewModel.canSubmit)
    }

    func testSubmitSuccessUsesAndroidPayloadAndCompletion() async throws {
        let scenicJSON = """
        {"code":100000,"msg":"success","data":{"total":1,"list":[{"id":101,"name":"新景区"}]}}
        """.data(using: .utf8)!
        let submitJSON = """
        {"code":100000,"msg":"success","data":{"code":"AP999"}}
        """.data(using: .utf8)!
        let session = MockURLSession(responses: [scenicJSON, submitJSON])
        let api = HomeAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "", isSelected: true))
        await viewModel.loadAvailableScenics(api: api)
        if let scenic = viewModel.availableScenics.first(where: { $0.id == 101 }) {
            viewModel.toggleScenic(scenic)
        }

        var didSubmit = false
        viewModel.onSubmitSuccess = { didSubmit = true }
        XCTAssertTrue(viewModel.canSubmit)
        await viewModel.submit(api: api)

        XCTAssertTrue(didSubmit)
        XCTAssertFalse(viewModel.isSubmitting)
        XCTAssertEqual(session.requests.last?.url?.path, "/api/yf-handset-app/photog/role-apply/submit")
        let body = try XCTUnwrap(session.requests.last?.httpBody)
        let object = try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any])
        XCTAssertEqual(object["role_id"] as? Int, 10)
        XCTAssertEqual(object["scenic_id"] as? [Int], [101])
    }

    func testSubmitFailureRestoresEnabledButtonAndKeepsSelection() async throws {
        let scenicJSON = """
        {"code":100000,"msg":"success","data":{"total":1,"list":[{"id":101,"name":"新景区"}]}}
        """.data(using: .utf8)!
        let failureJSON = #"{"code":100001,"msg":"failed","data":null}"#.data(using: .utf8)!
        let api = HomeAPI(
            client: APIClient(
                environment: .testing,
                session: MockURLSession(responses: [scenicJSON, failureJSON])
            )
        )
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "", isSelected: true))
        await viewModel.loadAvailableScenics(api: api)
        viewModel.toggleScenic(try XCTUnwrap(viewModel.availableScenics.first))
        var message: String?
        viewModel.onShowMessage = { message = $0 }

        await viewModel.submit(api: api)

        XCTAssertEqual(message, "提交失败，请稍后重试")
        XCTAssertFalse(viewModel.isSubmitting)
        XCTAssertTrue(viewModel.canSubmit)
        XCTAssertEqual(viewModel.selectedScenicIds, [101])
    }

    func testSwitchingRoleClearsScenicSelectionAndLoadedOptions() async throws {
        let scenicJSON = """
        {"code":100000,"msg":"success","data":{"total":1,"list":[{"id":101,"name":"新景区"}]}}
        """.data(using: .utf8)!
        let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [scenicJSON])))
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "", isSelected: true))
        await viewModel.loadAvailableScenics(api: api)
        viewModel.toggleScenic(try XCTUnwrap(viewModel.availableScenics.first))

        viewModel.selectRole(RoleOption(id: 11, name: "门店管理员", description: "", isSelected: true))

        XCTAssertEqual(viewModel.selectedRoleId, 11)
        XCTAssertTrue(viewModel.selectedScenicIds.isEmpty)
        XCTAssertTrue(viewModel.availableScenics.isEmpty)
    }

    func testAvailableRolesAreDeduplicatedByRoleID() {
        appStore.permissions.saveRolePermissionList([
            RolePermissionResponse(
                role: RoleInfo(id: 10, name: "摄影师", roleCode: "PHOTOGRAPHER", notes: "拍摄"),
                scenic: [ScenicInfo(id: 100, name: "景区A", status: 1)]
            ),
            RolePermissionResponse(
                role: RoleInfo(id: 10, name: "摄影师", roleCode: "PHOTOGRAPHER", notes: "拍摄"),
                scenic: [ScenicInfo(id: 101, name: "景区B", status: 1)]
            ),
        ])

        let viewModel = PermissionApplyViewModel(appStore: appStore)

        XCTAssertEqual(viewModel.availableRoles.map(\.id), [10])
        XCTAssertEqual(viewModel.rolePermissionList.count, 2)
    }

    func testScenicSearchAndDeletionConfirmation() async throws {
        let scenicJSON = """
        {"code":100000,"msg":"success","data":{"total":3,"list":[{"id":100,"name":"已有景区"},{"id":101,"name":"西湖景区"},{"id":102,"name":"东湖景区"}]}}
        """.data(using: .utf8)!
        let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [scenicJSON])))
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "", isSelected: true))
        await viewModel.loadAvailableScenics(api: api)

        viewModel.updateScenicSearchQuery("西湖")
        XCTAssertEqual(viewModel.filteredScenics.map(\.id), [101])
        let scenic = try XCTUnwrap(viewModel.filteredScenics.first)
        viewModel.toggleScenic(scenic)
        XCTAssertTrue(viewModel.canSubmit)

        viewModel.removeSelectedScenic(id: scenic.id, name: scenic.name)
        XCTAssertEqual(viewModel.pendingDeletion, .scenic(id: 101, name: "西湖景区"))
        viewModel.cancelDeletion()
        XCTAssertEqual(viewModel.selectedScenicIds, [101])

        viewModel.removeSelectedScenic(id: scenic.id, name: scenic.name)
        viewModel.confirmDeletion()
        XCTAssertTrue(viewModel.selectedScenicIds.isEmpty)
        XCTAssertFalse(viewModel.canSubmit)
    }

    func testRoleDeletionRequiresConfirmationAndClearsScenics() async throws {
        let scenicJSON = """
        {"code":100000,"msg":"success","data":{"total":1,"list":[{"id":101,"name":"新景区"}]}}
        """.data(using: .utf8)!
        let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [scenicJSON])))
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        viewModel.selectRole(RoleOption(id: 10, name: "摄影师", description: "", isSelected: true))
        await viewModel.loadAvailableScenics(api: api)
        viewModel.toggleScenic(try XCTUnwrap(viewModel.availableScenics.first))

        viewModel.clearRoleSelection()
        XCTAssertEqual(viewModel.pendingDeletion, .role(name: "摄影师"))
        XCTAssertEqual(viewModel.selectedRoleId, 10)
        viewModel.confirmDeletion()

        XCTAssertNil(viewModel.selectedRoleId)
        XCTAssertTrue(viewModel.selectedScenicIds.isEmpty)
        XCTAssertTrue(viewModel.availableScenics.isEmpty)
    }

    func testExistingRoleListAndScenicDialogState() {
        let viewModel = PermissionApplyViewModel(appStore: appStore)
        XCTAssertEqual(viewModel.rolePermissionList.count, 1)
        XCTAssertEqual(viewModel.availableRoles.first?.description, "拍摄")

        let scenics = viewModel.rolePermissionList[0].scenic
        viewModel.showScenicList(roleName: "摄影师", scenics: scenics)
        XCTAssertTrue(viewModel.showScenicListDialog)
        XCTAssertEqual(viewModel.dialogRoleName, "摄影师")
        XCTAssertEqual(viewModel.dialogScenicList.map(\.id), [100])
        viewModel.hideScenicList()
        XCTAssertFalse(viewModel.showScenicListDialog)
    }
}
