import Foundation
import LocalAuthentication
import SwiftUI
import Observation

/// Face ID 启动锁的运行时控制器(单例)。
///
/// 设计见 `docs/superpowers/specs/2026-05-30-faceid-app-lock-design.md`。
/// 红线对齐(CLAUDE.md §10.2):只用系统 `LocalAuthentication`,不自造任何密码学。
///
/// 单例写法与 `ModelDownloadService.shared` 一致:`@MainActor @Observable`。
/// UI(`AppLockContainer` / `MeView` / `LockScreenView`)只观察本类的 observable 状态,
/// 通过 `handleAppear` / `handleScenePhase` / `authenticate` 等方法驱动。
@MainActor
@Observable
final class AppLock {
    static let shared = AppLock()

    /// 后台超过该时长再回前台 → 重锁。
    static let gracePeriod: TimeInterval = 60

    /// 启用开关持久化 key,与 `MeView` 的 `@AppStorage` 同源。
    static let enabledKey = "faceIDLockEnabled"

    // MARK: - Observable 运行态

    /// 当前是否处于锁定(需认证才能进入)。
    private(set) var isLocked = false

    /// 进入任务切换器 / 后台时是否盖隐私遮罩(仅锁开启时为真)。
    private(set) var showsPrivacyCover = false

    /// 设备是否可用生物识别或密码认证(无密码设备为 false)。
    private(set) var biometryAvailable = false

    /// 认证按钮 / 副标题文案:"Face ID" / "Touch ID" / "密码"。
    private(set) var biometryLabel = String(appLoc: "密码")

    // MARK: - 非观察内部态

    /// 是否已开启启动锁。读写 UserDefaults(与 MeView 的 @AppStorage 同 key)。
    /// 不需要 observable —— UI 侧用 @AppStorage 观察这个 key 的变化。
    var enabled: Bool {
        get { UserDefaults.standard.bool(forKey: Self.enabledKey) }
        set { UserDefaults.standard.set(newValue, forKey: Self.enabledKey) }
    }

    private var lastBackgroundedAt: Date?
    private var didColdLaunchLock = false
    private var isAuthenticating = false

    private init() {
        refreshAvailability()
    }

    // MARK: - 能力探测

    /// 刷新「设备能否认证」与文案。进设置页 / 容器出现时调。
    func refreshAvailability() {
        let ctx = LAContext()
        var error: NSError?
        // .deviceOwnerAuthentication:设备设了密码即为 true(含生物识别 + 密码兜底)。
        biometryAvailable = ctx.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
        // biometryType 只有在 canEvaluatePolicy 调用后才有效。
        switch ctx.biometryType {
        case .faceID:  biometryLabel = "Face ID"
        case .touchID: biometryLabel = "Touch ID"
        default:       biometryLabel = String(appLoc: "密码")
        }
    }

    // MARK: - 生命周期驱动(由 AppLockContainer 调)

    /// 冷启动:容器首次出现时调一次。
    func handleAppear() {
        refreshAvailability()
        guard enabled, !didColdLaunchLock else { return }
        didColdLaunchLock = true
        isLocked = true
        Task { await authenticate() }
    }

    /// scenePhase 变化驱动。
    func handleScenePhase(_ phase: ScenePhase) {
        switch phase {
        case .inactive:
            // 任务切换器 / 系统弹窗打断:盖遮罩(已锁定时锁屏本身就是遮罩)。
            showsPrivacyCover = enabled && !isLocked

        case .background:
            lastBackgroundedAt = Date()
            showsPrivacyCover = enabled

        case .active:
            showsPrivacyCover = false
            if enabled, !isLocked,
               let since = lastBackgroundedAt,
               Date().timeIntervalSince(since) > Self.gracePeriod {
                isLocked = true
            }
            if isLocked { Task { await authenticate() } }
            lastBackgroundedAt = nil

        @unknown default:
            break
        }
    }

    // MARK: - 认证

    /// 触发系统认证。成功 → 解锁;失败/取消 → 保持锁定。`isAuthenticating` 防重入,
    /// 避免容器与锁屏 onAppear 同时各弹一次。
    func authenticate() async {
        guard isLocked, !isAuthenticating else { return }
        isAuthenticating = true
        defer { isAuthenticating = false }

        let ctx = LAContext()
        ctx.localizedFallbackTitle = String(appLoc: "输入密码")
        do {
            let ok = try await ctx.evaluatePolicy(
                .deviceOwnerAuthentication,
                localizedReason: String(appLoc: "解锁康康,查看你的健康档案")
            )
            if ok { isLocked = false }
        } catch {
            // 失败/取消:停留锁屏,用户可点「解锁」重试。不抛给 UI。
        }
    }

    // MARK: - 设置开关(MeView 调)

    /// 开启:先认证一次(验证设备可用 + 确认本人),成功才置 `enabled`。
    /// 返回最终是否已开启。
    @discardableResult
    func enableWithAuth() async -> Bool {
        let ctx = LAContext()
        ctx.localizedFallbackTitle = String(appLoc: "输入密码")
        do {
            let ok = try await ctx.evaluatePolicy(
                .deviceOwnerAuthentication,
                localizedReason: String(appLoc: "验证你本人,开启 Face ID 启动锁")
            )
            if ok {
                enabled = true
                return true
            }
        } catch {
            // 取消/失败:不开启。
        }
        return false
    }

    /// 关闭:直接关(此刻已在 App 内、本次已通过认证)。
    func disable() {
        enabled = false
    }
}
