//
//  AccountContextLoader.swift
//  suixinkan
//
//  Created by Codex on 2026/6/22.
//

import Foundation

@MainActor
/// 用户资料服务协议，抽象用户信息读取能力以便测试替换。
protocol UserProfileServing {
    /// 获取当前登录账号的用户资料。
    func userInfo() async throws -> UserInfoResponse
}

@MainActor
/// 账号上下文加载器，统一装配用户资料、角色权限、景区和门店上下文。
struct AccountContextLoader {
    /// 刷新完整账号上下文；登录成功等场景仍需要同步拉齐全部数据。
    func refresh(
        accountContext: AccountContext,
        permissionContext: PermissionContext,
        profileAPI: UserProfileServing,
        accountContextAPI: AccountContextServing,
        cachedCurrentRoleCode: String? = nil,
        cachedLegacyRoleId: Int? = nil,
        cachedCurrentScenicId: Int? = nil,
        cachedCurrentStoreId: Int? = nil,
        explicitScopeSelection: Bool = false
    ) async throws {
        try await restorePermissions(
            permissionContext: permissionContext,
            accountContext: accountContext,
            accountContextAPI: accountContextAPI,
            cachedCurrentRoleCode: cachedCurrentRoleCode,
            cachedLegacyRoleId: cachedLegacyRoleId,
            cachedCurrentScenicId: cachedCurrentScenicId,
            cachedCurrentStoreId: cachedCurrentStoreId,
            explicitScopeSelection: explicitScopeSelection
        )
        try await refreshSupplementalContext(
            accountContext: accountContext,
            permissionContext: permissionContext,
            profileAPI: profileAPI,
            accountContextAPI: accountContextAPI,
            cachedCurrentScenicId: cachedCurrentScenicId,
            cachedCurrentStoreId: cachedCurrentStoreId,
            explicitScopeSelection: explicitScopeSelection
        )
    }

    /// 冷启动阻塞阶段：仅恢复角色权限，确保首页菜单和权限控制可用。
    func restorePermissions(
        permissionContext: PermissionContext,
        accountContext: AccountContext,
        accountContextAPI: AccountContextServing,
        cachedCurrentRoleCode: String? = nil,
        cachedLegacyRoleId: Int? = nil,
        cachedCurrentScenicId: Int? = nil,
        cachedCurrentStoreId: Int? = nil,
        explicitScopeSelection: Bool = false
    ) async throws {
        let rolePermissions = try await accountContextAPI.rolePermissions()
        permissionContext.replaceRolePermissions(
            rolePermissions,
            currentRoleCode: cachedCurrentRoleCode,
            cachedLegacyRoleId: cachedLegacyRoleId
        )

        let roleScenicScopes = permissionContext.currentRoleScenicScopes()
        guard accountContext.scenicScopes.isEmpty, !roleScenicScopes.isEmpty else { return }
        accountContext.replaceScopes(
            scenic: roleScenicScopes,
            stores: accountContext.storeScopes,
            currentScenicId: cachedCurrentScenicId,
            currentStoreId: cachedCurrentStoreId,
            explicitSelection: explicitScopeSelection
        )
    }

    /// 冷启动后台阶段：补齐用户资料、景区和门店上下文。
    func refreshSupplementalContext(
        accountContext: AccountContext,
        permissionContext: PermissionContext,
        profileAPI: UserProfileServing,
        accountContextAPI: AccountContextServing,
        cachedCurrentScenicId: Int? = nil,
        cachedCurrentStoreId: Int? = nil,
        explicitScopeSelection: Bool = false
    ) async throws {
        let userInfo = try await profileAPI.userInfo()
        let scenicResponse = await loadScenicList(
            accountContextAPI: accountContextAPI,
            rolePermissions: permissionContext.rolePermissions
        )
        let storesResponse = await loadStores(accountContextAPI: accountContextAPI)
        let profile = makeProfile(from: userInfo, fallback: accountContext.profile)
        let scenicScopes = scopedScenics(
            permissionContext: permissionContext,
            scenicResponse: scenicResponse
        )
        let fetchedStoreScopes = storesResponse?.list.map { store in
            BusinessScope(id: store.id, name: store.name, kind: .store, parentScenicId: store.scenicId)
        } ?? []
        // 门店列表接口失败或为空时，保留登录/切换时已写入的门店作用域，避免门店账号展示回退成景区名。
        let storeScopes = fetchedStoreScopes.isEmpty ? accountContext.storeScopes : fetchedStoreScopes

        accountContext.replaceProfile(profile)
        accountContext.replaceScopes(
            scenic: scenicScopes,
            stores: storeScopes,
            currentScenicId: resolvedScenicId(
                cachedCurrentScenicId: cachedCurrentScenicId,
                accountContext: accountContext,
                explicitScopeSelection: explicitScopeSelection
            ),
            currentStoreId: resolvedStoreId(
                cachedCurrentStoreId: cachedCurrentStoreId,
                accountContext: accountContext,
                explicitScopeSelection: explicitScopeSelection
            ),
            explicitSelection: explicitScopeSelection
        )
    }

    /// 解析刷新时应使用的景区 ID。
    private func resolvedScenicId(
        cachedCurrentScenicId: Int?,
        accountContext: AccountContext,
        explicitScopeSelection: Bool
    ) -> Int? {
        if explicitScopeSelection {
            return cachedCurrentScenicId ?? accountContext.currentScenic?.id
        }
        return cachedCurrentScenicId ?? accountContext.currentScenic?.id
    }

    /// 解析刷新时应使用的门店 ID；景区账号在显式选择模式下必须清空门店。
    private func resolvedStoreId(
        cachedCurrentStoreId: Int?,
        accountContext: AccountContext,
        explicitScopeSelection: Bool
    ) -> Int? {
        if explicitScopeSelection {
            if accountContext.accountType == V9ScenicUser.accountTypeValue {
                return nil
            }
            return cachedCurrentStoreId
        }
        return cachedCurrentStoreId ?? accountContext.currentStore?.id
    }

    /// 景区列表接口失败时，从角色权限中的景区数据去重构造兜底列表。
    private func loadScenicList(
        accountContextAPI: AccountContextServing,
        rolePermissions: [RolePermissionResponse]
    ) async -> ScenicListAllResponse {
        do {
            return try await accountContextAPI.scenicListAll()
        } catch {
            let fallback = uniqueScenics(from: rolePermissions)
            return ScenicListAllResponse(total: fallback.count, list: fallback)
        }
    }

    /// 门店列表接口失败时返回 nil，不阻断登录和恢复流程。
    private func loadStores(accountContextAPI: AccountContextServing) async -> ListPayload<StoreItem>? {
        try? await accountContextAPI.storeAll()
    }

    /// 根据当前角色优先返回角色可访问景区，角色无景区时使用景区列表接口结果。
    private func scopedScenics(
        permissionContext: PermissionContext,
        scenicResponse: ScenicListAllResponse
    ) -> [BusinessScope] {
        let roleScenics = permissionContext.currentRoleScenicScopes()
        if !roleScenics.isEmpty {
            return roleScenics
        }
        return scenicResponse.list.map {
            BusinessScope(id: $0.id, name: $0.name, kind: .scenic)
        }
    }

    /// 从角色权限中去重生成景区列表。
    private func uniqueScenics(from rolePermissions: [RolePermissionResponse]) -> [ScenicListItem] {
        var seen = Set<Int>()
        var result: [ScenicListItem] = []
        for permission in rolePermissions {
            for scenic in permission.scenic where seen.insert(scenic.id).inserted {
                result.append(ScenicListItem(id: scenic.id, name: scenic.name))
            }
        }
        return result
    }

    /// 将用户资料接口响应转换为全局账号资料。
    private func makeProfile(from userInfo: UserInfoResponse, fallback: AccountProfile?) -> AccountProfile {
        AccountProfile(
            userId: fallback?.userId ?? "",
            displayName: nonEmpty(userInfo.nickname) ?? nonEmpty(userInfo.realName) ?? fallback?.displayName ?? "未设置昵称",
            phone: nonEmpty(userInfo.phone) ?? fallback?.phone,
            avatarURL: nonEmpty(userInfo.avatar) ?? fallback?.avatarURL
        )
    }

    /// 去除空白字符后返回非空字符串。
    private func nonEmpty(_ value: String?) -> String? {
        let text = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        return text.isEmpty ? nil : text
    }
}
