//
//  SessionBootstrapperTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/30.
//

import XCTest
@testable import suixinkan

@MainActor
/// 冷启动恢复测试，覆盖阻塞权限恢复与后台补充刷新。
final class SessionBootstrapperTests: XCTestCase {
    /// 测试无 token 时直接进入未登录状态。
    func testRestoreWithoutTokenLogsOut() async {
        let defaults = makeDefaults()
        let tokenStore = SessionTokenStore(defaults: defaults, key: "test.token")
        let snapshotStore = AccountSnapshotStore(defaults: defaults, key: "test.snapshot")
        let bootstrapper = SessionBootstrapper(tokenStore: tokenStore, snapshotStore: snapshotStore)
        let appSession = AppSession()
        let accountContext = AccountContext()
        let permissionContext = PermissionContext()
        let toastCenter = ToastCenter()

        await bootstrapper.restore(
            appSession: appSession,
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: MockUserProfileAPI(),
            accountContextAPI: MockAccountContextAPI(),
            toastCenter: toastCenter
        )

        XCTAssertEqual(appSession.phase, .loggedOut)
    }

    /// 测试有 token 时阻塞等待 rolePermissions 后立即进入 loggedIn。
    func testRestoreMarksLoggedInAfterRolePermissions() async {
        let defaults = makeDefaults()
        let tokenStore = SessionTokenStore(defaults: defaults, key: "test.token")
        try? tokenStore.save("token-1")
        let snapshotStore = AccountSnapshotStore(defaults: defaults, key: "test.snapshot")
        let bootstrapper = SessionBootstrapper(tokenStore: tokenStore, snapshotStore: snapshotStore)
        let appSession = AppSession()
        let accountContext = AccountContext()
        let permissionContext = PermissionContext()
        let toastCenter = ToastCenter()
        let api = MockAccountContextAPI()
        api.roles = [
            RolePermissionResponse(
                role: RoleInfo(id: 7, name: "运营", permission: [PermissionItem(id: 1, name: "首页", uri: "/home")]),
                scenic: [ScenicInfo(id: 88, name: "东湖景区")]
            )
        ]

        await bootstrapper.restore(
            appSession: appSession,
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: MockUserProfileAPI(),
            accountContextAPI: api,
            toastCenter: toastCenter
        )

        XCTAssertEqual(appSession.phase, .loggedIn)
        XCTAssertEqual(permissionContext.currentRole?.id, 7)
        XCTAssertTrue(permissionContext.canAccess("/home"))

        let profileExpectation = expectation(description: "profile loaded in background")
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            if accountContext.profile?.displayName == "测试用户" {
                profileExpectation.fulfill()
            }
        }
        await fulfillment(of: [profileExpectation], timeout: 2)
    }

    /// 测试 rolePermissions 鉴权失败时会清空本地登录态。
    func testRestoreLogsOutWhenRolePermissionsExpired() async {
        let defaults = makeDefaults()
        let tokenStore = SessionTokenStore(defaults: defaults, key: "test.token")
        try? tokenStore.save("token-1")
        let snapshotStore = AccountSnapshotStore(defaults: defaults, key: "test.snapshot")
        snapshotStore.save(AccountSnapshot(profile: AccountProfile(userId: "1", displayName: "本地用户")))
        let bootstrapper = SessionBootstrapper(tokenStore: tokenStore, snapshotStore: snapshotStore)
        let appSession = AppSession()
        let accountContext = AccountContext()
        let permissionContext = PermissionContext()
        let toastCenter = ToastCenter()
        let api = MockAccountContextAPI()
        api.roleError = APIError.httpStatus(401, "登录状态已失效")

        await bootstrapper.restore(
            appSession: appSession,
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: MockUserProfileAPI(),
            accountContextAPI: api,
            toastCenter: toastCenter
        )

        XCTAssertEqual(appSession.phase, .loggedOut)
        XCTAssertNil(tokenStore.load())
        XCTAssertNil(snapshotStore.load())
    }

    private func makeDefaults() -> UserDefaults {
        let suiteName = "SessionBootstrapperTests.\(UUID().uuidString)"
        let defaults = UserDefaults(suiteName: suiteName)!
        defaults.removePersistentDomain(forName: suiteName)
        return defaults
    }
}

@MainActor
private final class MockUserProfileAPI: UserProfileServing {
    func userInfo() async throws -> UserInfoResponse {
        try await Task.sleep(nanoseconds: 100_000_000)
        return UserInfoResponse(nickname: "测试用户", roleName: "运营")
    }
}

@MainActor
private final class MockAccountContextAPI: AccountContextServing {
    var roles: [RolePermissionResponse] = []
    var roleError: Error?

    func rolePermissions() async throws -> [RolePermissionResponse] {
        if let roleError { throw roleError }
        return roles
    }

    func scenicListAll() async throws -> ScenicListAllResponse {
        ScenicListAllResponse(total: 0, list: [])
    }

    func storeAll() async throws -> ListPayload<StoreItem> {
        ListPayload(total: 0, list: [])
    }

    func scenicSpotListAll(scenicId: Int) async throws -> ListPayload<ScenicSpotItem> {
        ListPayload(total: 0, list: [])
    }
}
