//
//  PermissionApplyViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/23.
//

import Foundation
import Observation

/// 权限申请角色选项实体，表示可申请的一个业务角色。
struct PermissionRoleOption: Equatable, Identifiable {
    let id: Int
    let name: String
    let notes: String
}

/// 权限申请景区选项实体，表示可申请或已拥有权限的一个景区。
struct PermissionScenicOption: Equatable, Identifiable {
    let id: Int
    let name: String
    var selected: Bool
    var disabled: Bool
}

/// 已有角色权限展示实体，表示一个角色及其已拥有景区。
struct ExistingRolePermissionInfo: Equatable, Identifiable {
    let id: Int
    let name: String
    let scenics: [ScenicInfo]
}

@MainActor
@Observable
/// 权限申请 ViewModel，负责角色/景区选择和提交审核。
final class PermissionApplyViewModel {
    var roleOptions: [PermissionRoleOption] = []
    var selectedRoleId: Int?
    var scenicOptions: [PermissionScenicOption] = []
    var loadingScenics = false
    var scenicLoadFailed = false
    var scenicLoadFailureReason: String?
    var submitting = false
    var message: String?

    private let initialPending: RoleApplyPendingResponse?
    private var rolePermissions: [RolePermissionResponse] = []
    private var selectedIds: Set<Int> = []

    /// 初始化权限申请 ViewModel，可传入驳回申请用于编辑回填。
    init(initialPending: RoleApplyPendingResponse? = nil) {
        self.initialPending = initialPending
    }

    /// 返回是否允许提交审核。
    var canSubmit: Bool {
        !submitting && selectedRoleId != nil && !selectedIds.isEmpty
    }

    /// 返回已选择景区数量。
    var selectedCount: Int {
        selectedIds.count
    }

    /// 返回已选择景区 ID 集合，供测试和提交校验使用。
    var selectedScenicIds: Set<Int> {
        selectedIds
    }

    /// 返回景区选择摘要文案。
    var selectedScenicSummary: String {
        let names = scenicOptions
            .filter { !$0.disabled && selectedIds.contains($0.id) }
            .map(\.name)
        if names.isEmpty { return "请选择景区" }
        if names.count == 1 { return names[0] }
        return "已选择 \(names.count) 个景区"
    }

    /// 返回当前选择角色的说明。
    var selectedRoleNotes: String? {
        guard let selectedRoleId else { return nil }
        return roleOptions.first(where: { $0.id == selectedRoleId })?.notes
    }

    /// 返回当前选择角色名称。
    var selectedRoleName: String? {
        guard let selectedRoleId else { return nil }
        return roleOptions.first(where: { $0.id == selectedRoleId })?.name
    }

    /// 返回已有角色权限展示列表。
    var existingRoleInfos: [ExistingRolePermissionInfo] {
        rolePermissions.map {
            ExistingRolePermissionInfo(id: $0.role.id, name: $0.role.name, scenics: $0.scenic)
        }
    }

    /// 从权限上下文启动表单数据。
    func bootstrap(rolePermissions: [RolePermissionResponse]) {
        self.rolePermissions = rolePermissions
        roleOptions = uniqueRoles(from: rolePermissions)
        if let pending = initialPending {
            selectedRoleId = pending.roleId
            selectedIds = Set(pending.scenicList.map(\.id))
        }
    }

    /// 选择角色，并清空当前景区选择等待重新加载。
    func selectRole(id: Int) {
        selectedRoleId = id
        selectedIds.removeAll()
        scenicOptions = []
        scenicLoadFailed = false
        scenicLoadFailureReason = nil
    }

    /// 按当前角色加载可申请景区列表。
    func loadScenicListIfNeeded(api: any ScenicPermissionServing, force: Bool = false) async {
        guard selectedRoleId != nil else { return }
        if !force && !scenicOptions.isEmpty { return }
        loadingScenics = true
        scenicLoadFailed = false
        scenicLoadFailureReason = nil
        defer { loadingScenics = false }
        do {
            let list = try await api.scenicListAll().list
            let disabledIds = Set(currentRolePermission()?.scenic.map(\.id) ?? [])
            scenicOptions = list.map { scenic in
                let selected = disabledIds.contains(scenic.id) || selectedIds.contains(scenic.id)
                return PermissionScenicOption(id: scenic.id, name: scenic.name, selected: selected, disabled: disabledIds.contains(scenic.id))
            }
            selectedIds = Set(scenicOptions.filter { $0.selected && !$0.disabled }.map(\.id))
        } catch {
            scenicOptions = []
            selectedIds.removeAll()
            scenicLoadFailed = true
            scenicLoadFailureReason = error.localizedDescription
            message = "景区列表加载失败，请重试"
        }
    }

    /// 切换一个景区的选择状态，已有权限的景区不可切换。
    func toggleScenic(id: Int) {
        guard let index = scenicOptions.firstIndex(where: { $0.id == id }), !scenicOptions[index].disabled else { return }
        scenicOptions[index].selected.toggle()
        if scenicOptions[index].selected {
            selectedIds.insert(id)
        } else {
            selectedIds.remove(id)
        }
    }

    /// 提交角色权限申请。
    func submit(api: any ScenicPermissionServing) async {
        guard let roleId = selectedRoleId, !selectedIds.isEmpty, !submitting else { return }
        submitting = true
        defer { submitting = false }
        do {
            try await api.roleApplySubmit(roleId: roleId, scenicIds: Array(selectedIds).sorted())
            message = "提交成功，等待审核"
        } catch {
            message = error.localizedDescription
        }
    }

    /// 生成去重后的角色选项。
    private func uniqueRoles(from list: [RolePermissionResponse]) -> [PermissionRoleOption] {
        var seen = Set<Int>()
        return list.compactMap { item in
            guard seen.insert(item.role.id).inserted else { return nil }
            return PermissionRoleOption(id: item.role.id, name: item.role.name, notes: item.role.notes ?? "")
        }
    }

    /// 返回当前选择角色对应的权限实体。
    private func currentRolePermission() -> RolePermissionResponse? {
        guard let selectedRoleId else { return nil }
        return rolePermissions.first { $0.role.id == selectedRoleId }
    }
}

@MainActor
@Observable
/// 权限申请状态 ViewModel，负责读取审核中的或指定编号的权限申请。
final class PermissionApplyStatusViewModel {
    var loading = false
    var pending: RoleApplyPendingResponse?
    var loadFailed = false
    var loadFailureReason: String?

    /// 加载权限申请状态；有申请编号时优先匹配编号，否则展示审核中或驳回记录。
    func load(api: any ScenicPermissionServing, applyCode: String?) async {
        loading = true
        loadFailed = false
        loadFailureReason = nil
        defer { loading = false }
        do {
            let all = try await api.roleApplyAll()
            let normalizedCode = applyCode?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
            if !normalizedCode.isEmpty {
                pending = all.first { $0.code == normalizedCode }
            } else {
                pending = all.first { $0.status == 1 || $0.status == 3 }
            }
        } catch {
            pending = nil
            loadFailed = true
            loadFailureReason = error.localizedDescription
        }
    }
}
