//
//  AccountContextLoaderTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/22.
//

import XCTest
@testable import suixinkan

@MainActor
/// 账号上下文加载器测试，覆盖权限、景区兜底和门店容错。
final class AccountContextLoaderTests: XCTestCase {
    /// 测试权限、用户资料、景区和门店成功时会写入上下文。
    func testRefreshLoadsRolePermissionScenicAndStoreContext() async throws {
        let accountContext = AccountContext()
        let permissionContext = PermissionContext()
        let api = MockAccountContextAPI()
        api.roles = [
            RolePermissionResponse(
                role: RoleInfo(id: 7, name: "运营", permission: [PermissionItem(id: 1, name: "首页", uri: "/home")]),
                scenic: [ScenicInfo(id: 88, name: "东湖景区")]
            )
        ]
        api.scenicResponse = ScenicListAllResponse(total: 1, list: [ScenicListItem(id: 88, name: "东湖景区")])
        api.storeResponse = ListPayload(total: 1, list: [StoreItem(id: 66, scenicId: 88, name: "东湖门店")])

        try await AccountContextLoader().refresh(
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: MockUserProfileAPI(),
            accountContextAPI: api
        )

        XCTAssertEqual(accountContext.profile?.displayName, "测试用户")
        XCTAssertEqual(permissionContext.currentRole?.id, 7)
        XCTAssertTrue(permissionContext.canAccess("/home"))
        XCTAssertEqual(accountContext.currentScenic?.id, 88)
        XCTAssertEqual(accountContext.currentStore?.id, 66)
    }

    /// 测试景区接口失败时会从角色权限景区兜底。
    func testRefreshFallsBackToRoleScenicsWhenScenicAPIThrows() async throws {
        let accountContext = AccountContext()
        let permissionContext = PermissionContext()
        let api = MockAccountContextAPI()
        api.roles = [
            RolePermissionResponse(
                role: RoleInfo(id: 7, name: "运营"),
                scenic: [
                    ScenicInfo(id: 88, name: "东湖景区"),
                    ScenicInfo(id: 88, name: "东湖景区")
                ]
            )
        ]
        api.scenicError = APIError.networkFailed("景区接口失败")

        try await AccountContextLoader().refresh(
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: MockUserProfileAPI(),
            accountContextAPI: api
        )

        XCTAssertEqual(accountContext.scenicScopes.map(\.id), [88])
    }

    /// 测试门店接口失败时不阻断上下文加载。
    func testRefreshAllowsStoreFailure() async throws {
        let accountContext = AccountContext()
        let permissionContext = PermissionContext()
        let api = MockAccountContextAPI()
        api.roles = [
            RolePermissionResponse(
                role: RoleInfo(id: 7, name: "运营"),
                scenic: [ScenicInfo(id: 88, name: "东湖景区")]
            )
        ]
        api.storeError = APIError.networkFailed("门店接口失败")

        try await AccountContextLoader().refresh(
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: MockUserProfileAPI(),
            accountContextAPI: api
        )

        XCTAssertEqual(accountContext.currentScenic?.id, 88)
        XCTAssertTrue(accountContext.storeScopes.isEmpty)
        XCTAssertNil(accountContext.currentStore)
    }
}

@MainActor
/// 用户资料服务测试替身，返回固定用户资料。
private final class MockUserProfileAPI: UserProfileServing {
    /// 获取固定用户资料。
    func userInfo() async throws -> UserInfoResponse {
        UserInfoResponse(nickname: "测试用户", roleName: "运营")
    }
}

@MainActor
/// 账号上下文服务测试替身，可分别控制各接口成功或失败。
private final class MockAccountContextAPI: AccountContextServing {
    var roles: [RolePermissionResponse] = []
    var scenicResponse = ScenicListAllResponse(total: 0, list: [])
    var storeResponse = ListPayload(total: 0, list: [StoreItem]())
    var scenicError: Error?
    var storeError: Error?

    /// 获取测试角色权限。
    func rolePermissions() async throws -> [RolePermissionResponse] {
        roles
    }

    /// 获取测试景区列表，或按配置抛出错误。
    func scenicListAll() async throws -> ScenicListAllResponse {
        if let scenicError {
            throw scenicError
        }
        return scenicResponse
    }

    /// 获取测试门店列表，或按配置抛出错误。
    func storeAll() async throws -> ListPayload<StoreItem> {
        if let storeError {
            throw storeError
        }
        return storeResponse
    }

    /// 获取测试景点列表。
    func scenicSpotListAll(scenicId: Int) async throws -> ListPayload<ScenicSpotItem> {
        ListPayload(total: 0, list: [])
    }
}
