//
//  HomeLocationViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/29.
//

import Combine
import CoreLocation
import Foundation

/// 首页位置上报 ViewModel，负责在线状态、倒计时、GPS 上报和超时提醒。
@MainActor
final class HomeLocationViewModel: ObservableObject {
    @Published private(set) var isOnline = false
    @Published private(set) var secondsUntilReport = 0
    @Published private(set) var reminderMinutes = 0
    @Published private(set) var isReporting = false
    @Published var showTimeoutDialog = false
    @Published var showReminderDialog = false
    @Published var reminderDialogMessage = ""
    @Published private(set) var reportTimeText = ""

    private let api: any LocationReportServing
    private let store: LocationStateStore
    private var locationProvider: ForegroundLocationProvider?

    private var lastReportAnchorTime: TimeInterval?
    private var expireTimestamp: Int?
    private var lastOperationTime: TimeInterval = 0
    private var timeoutDialogTriggered = false
    private var reminderTriggered = false
    private var hasRestoredState = false

    private let operationIntervalSeconds = 30

    /// 倒计时展示文案（H:MM:SS）。
    var countdownDisplay: String {
        let hours = secondsUntilReport / 3600
        let minutes = (secondsUntilReport % 3600) / 60
        let seconds = secondsUntilReport % 60
        return "\(hours):\(String(format: "%02d", minutes)):\(String(format: "%02d", seconds))"
    }

    /// 初始化首页位置上报 ViewModel。
    nonisolated init(
        api: any LocationReportServing,
        store: LocationStateStore = LocationStateStore()
    ) {
        self.api = api
        self.store = store
    }

    /// 从本地存储恢复在线状态和倒计时。
    func restoreState() {
        guard !hasRestoredState else { return }
        hasRestoredState = true

        reminderMinutes = store.loadReminderMinutes()
        guard store.loadOnlineStatus() else {
            isOnline = false
            secondsUntilReport = 0
            expireTimestamp = nil
            showTimeoutDialog = false
            timeoutDialogTriggered = false
            return
        }

        let persistedExpireTime = store.loadExpireTime()
        if persistedExpireTime > 0 {
            let remaining = store.remainingSeconds(untilExpireTime: persistedExpireTime)
            guard remaining > 0 else {
                setOfflineLocally(clearPersisted: true)
                reminderDialogMessage = "距离上次上报已超过两小时，请立即重新上报位置。"
                showReminderDialog = true
                return
            }

            isOnline = true
            expireTimestamp = persistedExpireTime
            secondsUntilReport = remaining
            lastReportAnchorTime = store.loadLastReportTime()
            evaluateReminderThreshold(remainingSeconds: remaining, forceDialog: false)
            return
        }

        let lastTime = store.loadLastReportTime()
        guard lastTime > 0 else {
            setOfflineLocally()
            return
        }

        let remaining = store.remainingSeconds(since: lastTime)
        guard remaining > 0 else {
            setOfflineLocally(clearPersisted: true)
            reminderDialogMessage = "距离上次上报已超过两小时，请立即重新上报位置。"
            showReminderDialog = true
            return
        }

        isOnline = true
        secondsUntilReport = remaining
        lastReportAnchorTime = lastTime
        evaluateReminderThreshold(remainingSeconds: remaining, forceDialog: false)
    }

    /// 每秒驱动倒计时，并在到期时自动离线。
    func tick() {
        guard isOnline, secondsUntilReport > 0 else { return }
        secondsUntilReport -= 1

        if secondsUntilReport <= 0 {
            setOfflineLocally(clearPersisted: true)
            reminderDialogMessage = "距离上次上报已超过两小时，请立即重新上报位置。"
            showReminderDialog = true
            return
        }

        evaluateReminderThreshold(remainingSeconds: secondsUntilReport, forceDialog: false)
    }

    /// 更新提前提醒分钟数并持久化。
    func updateReminderMinutes(_ minutes: Int) {
        reminderMinutes = minutes
        store.saveReminderMinutes(minutes)
        reminderTriggered = false
        timeoutDialogTriggered = false

        if let expireTime = expireTimestamp {
            let remaining = store.remainingSeconds(untilExpireTime: expireTime)
            evaluateReminderThreshold(remainingSeconds: remaining, forceDialog: false)
        } else if let anchor = lastReportAnchorTime {
            let remaining = store.remainingSeconds(since: anchor)
            evaluateReminderThreshold(remainingSeconds: remaining, forceDialog: false)
        }
    }

    /// 确认切换在线状态：上线自动立即上报，下线提交离线上报。
    func confirmOnlineToggle(staffId: Int?, scenicId: Int?, toast: ToastCenter) async {
        guard enforceOperationInterval(for: .immediate, toast: toast) else { return }
        guard validateScope(staffId: staffId, scenicId: scenicId, toast: toast) else { return }

        if isOnline {
            setOfflineLocally(clearPersisted: false)
            store.saveOnlineStatus(false)
            await reportLocation(type: .offlineReport, staffId: staffId!, scenicId: scenicId!, toast: toast)
        } else {
            store.saveOnlineStatus(true)
            isOnline = true
            await reportLocation(type: .immediate, staffId: staffId!, scenicId: scenicId!, toast: toast)
        }
    }

    /// 首页立即上报：定位后提交 type=1。
    func startImmediateReport(staffId: Int?, scenicId: Int?, toast: ToastCenter) async {
        guard enforceOperationInterval(for: .immediate, toast: toast) else { return }
        guard validateScope(staffId: staffId, scenicId: scenicId, toast: toast) else { return }
        await reportLocation(type: .immediate, staffId: staffId!, scenicId: scenicId!, toast: toast)
    }

    /// 根据本地在线状态和倒计时判断是否需要弹出超时提醒。
    func checkLocationTimeout(staffId: Int?) async {
        guard isOnline, reminderMinutes > 0, secondsUntilReport > 0 else {
            showTimeoutDialog = false
            return
        }
        evaluateReminderThreshold(remainingSeconds: secondsUntilReport, forceDialog: true)
    }

    /// 关闭超时对话框并将用户置为离线。
    func dismissTimeoutDialog() {
        showTimeoutDialog = false
        timeoutDialogTriggered = false
        setOfflineLocally(clearPersisted: true)
    }

    /// 关闭提醒对话框。
    func dismissReminderDialog() {
        showReminderDialog = false
    }

    /// 详情页上报成功后同步全局倒计时。
    func handleReportSuccess(expired: Int, type: LocationReportType) {
        guard type != .offlineReport else { return }
        beginOnlineCountdown(expired: expired)
    }

    /// 提交指定坐标的上报，供详情页在已有坐标时复用。
    func reportCoordinate(
        latitude: Double,
        longitude: Double,
        address: String,
        type: LocationReportType,
        staffId: Int?,
        scenicId: Int?,
        toast: ToastCenter
    ) async -> Bool {
        guard !isReporting else { return false }
        if type != .offlineReport {
            guard enforceOperationInterval(for: type, toast: toast) else { return false }
        }
        guard validateScope(staffId: staffId, scenicId: scenicId, toast: toast) else { return false }

        isReporting = true
        defer { isReporting = false }

        do {
            let response = try await api.reportLocation(
                staffId: staffId!,
                latitude: latitude,
                longitude: longitude,
                address: address,
                type: type,
                scenicId: scenicId!
            )
            lastOperationTime = Date().timeIntervalSince1970 * 1000
            if type != .offlineReport {
                beginOnlineCountdown(expired: response.expired)
            }
            showTimeoutDialog = false
            timeoutDialogTriggered = false
            reminderTriggered = false
            return true
        } catch {
            toast.show("位置上报失败：\(error.localizedDescription)")
            return false
        }
    }

    /// 定位并上报指定类型。
    private func reportLocation(
        type: LocationReportType,
        staffId: Int,
        scenicId: Int,
        toast: ToastCenter
    ) async {
        guard hasLocationPermission() else {
            toast.show("需要位置权限才能上报位置")
            return
        }

        isReporting = true
        defer { isReporting = false }

        do {
            let location = try await activeLocationProvider().requestCurrentLocation()
            let response = try await api.reportLocation(
                staffId: staffId,
                latitude: location.latitude,
                longitude: location.longitude,
                address: location.address,
                type: type,
                scenicId: scenicId
            )
            lastOperationTime = Date().timeIntervalSince1970 * 1000
            if type != .offlineReport {
                beginOnlineCountdown(expired: response.expired)
                toast.show("位置上报成功")
            } else {
                toast.show("已切换到离线状态")
            }
            showTimeoutDialog = false
            timeoutDialogTriggered = false
            reminderTriggered = false
        } catch {
            toast.show(type == .offlineReport ? "离线上报失败：\(error.localizedDescription)" : "位置上报失败：\(error.localizedDescription)")
        }
    }

    /// 获取或创建前台定位 Provider。
    private func activeLocationProvider() -> ForegroundLocationProvider {
        if let locationProvider {
            return locationProvider
        }
        let provider = ForegroundLocationProvider()
        locationProvider = provider
        return provider
    }

    /// 上报成功后根据服务端 expired 启动在线倒计时。
    private func beginOnlineCountdown(expired: Int) {
        let now = Date().timeIntervalSince1970 * 1000
        let expireTime = store.normalizedExpireTimestamp(from: expired)
        lastReportAnchorTime = now
        expireTimestamp = expireTime
        store.saveLastReportTime(now)
        store.saveExpireTime(expireTime)
        store.saveOnlineStatus(true)
        isOnline = true
        secondsUntilReport = store.remainingSeconds(untilExpireTime: expireTime)
        reportTimeText = Self.timeFormatter.string(from: Date())
    }

    /// 本地置离线并可选清除持久化。
    private func setOfflineLocally(clearPersisted: Bool = true) {
        isOnline = false
        secondsUntilReport = 0
        lastReportAnchorTime = nil
        expireTimestamp = nil
        showTimeoutDialog = false
        timeoutDialogTriggered = false
        if clearPersisted {
            store.saveOnlineStatus(false)
            store.clearLastReportTime()
            store.clearExpireTime()
        }
    }

    /// 校验 staffId 和 scenicId。
    private func validateScope(staffId: Int?, scenicId: Int?, toast: ToastCenter) -> Bool {
        guard let scenicId, scenicId > 0 else {
            toast.show("请先选择景区")
            return false
        }
        guard let staffId, staffId > 0 else {
            toast.show("获取用户ID失败")
            return false
        }
        return true
    }

    /// 限制 30 秒内重复上报或切换（离任 type=3）。
    private func enforceOperationInterval(for type: LocationReportType, toast: ToastCenter) -> Bool {
        guard type != .offlineReport else { return true }
        let now = Date().timeIntervalSince1970 * 1000
        let elapsed = now - lastOperationTime
        guard lastOperationTime > 0, elapsed < Double(operationIntervalSeconds * 1000) else { return true }
        let remaining = Int((Double(operationIntervalSeconds * 1000) - elapsed) / 1000)
        toast.show("操作过于频繁，请\(max(remaining, 1))秒后再试")
        return false
    }

    /// 判断是否到达提前提醒阈值（仅在线且倒计时进入提醒窗口时触发）。
    private func evaluateReminderThreshold(remainingSeconds: Int, forceDialog: Bool) {
        guard isOnline, reminderMinutes > 0 else { return }
        let thresholdSeconds = reminderMinutes * 60
        guard remainingSeconds <= thresholdSeconds else { return }

        if !reminderTriggered {
            reminderTriggered = true
            reminderDialogMessage = "倒计时即将结束，请尽快重新上报位置。"
            showReminderDialog = true
        }

        if !timeoutDialogTriggered || forceDialog {
            timeoutDialogTriggered = true
            showTimeoutDialog = true
        }
    }

    /// 检查是否已授予前台定位权限。
    private func hasLocationPermission() -> Bool {
        switch CLLocationManager.authorizationStatus() {
        case .authorizedAlways, .authorizedWhenInUse:
            true
        default:
            false
        }
    }

    private static let timeFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        return formatter
    }()
}
