//
//  LocationReportService.swift
//  suixinkan
//

import CoreLocation
import Foundation

/// 位置上报成功结果。
struct LocationReportSuccessResult: Equatable {
    let reportTimeText: String
    let locationInfoText: String
    let shouldShowSuccessDialog: Bool
}

/// 位置上报共享业务逻辑，对齐 Android `HomeViewModel.reportLocation`。
final class LocationReportService {

    static let operationIntervalMillis: Int64 = 30_000
    static let manualReportDesiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyHundredMeters

    private(set) var lastOnlineStatusSwitchTime: Int64 = 0
    private(set) var lastLocationReportTime: Int64 = 0
    private(set) var isReporting = false

    private let appStore: AppStore
    private let locationStateStore: HomeLocationStateStore
    private let locationProvider: any LocationProviding

    init(
        appStore: AppStore = .shared,
        locationStateStore: HomeLocationStateStore,
        locationProvider: any LocationProviding = LocationProvider.shared
    ) {
        self.appStore = appStore
        self.locationStateStore = locationStateStore
        self.locationProvider = locationProvider
    }

    /// 切换在线/离线并触发位置上报。
    func switchOnlineStatus(api: HomeAPI) async throws -> LocationReportSuccessResult? {
        let now = currentTimestampMillis()
        if now - lastOnlineStatusSwitchTime < Self.operationIntervalMillis {
            let remaining = Int((Self.operationIntervalMillis - (now - lastOnlineStatusSwitchTime)) / 1000)
            throw LocationReportServiceError.tooFrequent(remainingSeconds: remaining)
        }
        guard appStore.session.currentScenicId > 0 else {
            throw LocationReportServiceError.missingScenic
        }

        let goingOnline = !locationStateStore.isOnline
        if goingOnline {
            _ = try await locationProvider.requestSnapshot()
            locationStateStore.updateOnlineStatus(true)
            lastOnlineStatusSwitchTime = now
            return try await reportWithCoordinates(
                api: api,
                latitude: nil,
                longitude: nil,
                address: nil,
                type: 1,
                showSuccessToast: true
            )
        }

        locationStateStore.updateOnlineStatus(false)
        lastOnlineStatusSwitchTime = now
        _ = try await reportWithCoordinates(
            api: api,
            latitude: nil,
            longitude: nil,
            address: nil,
            type: 3,
            showSuccessToast: false
        )
        return nil
    }

    /// 手动 GPS 上报。
    func manualReport(api: HomeAPI) async throws -> LocationReportSuccessResult {
        let now = currentTimestampMillis()
        if now - lastLocationReportTime < Self.operationIntervalMillis {
            let remaining = Int((Self.operationIntervalMillis - (now - lastLocationReportTime)) / 1000)
            throw LocationReportServiceError.tooFrequent(remainingSeconds: remaining)
        }
        return try await reportWithCoordinates(
            api: api,
            latitude: nil,
            longitude: nil,
            address: nil,
            type: 1,
            showSuccessToast: true,
            desiredAccuracy: Self.manualReportDesiredAccuracy
        )
    }

    /// 指定坐标上报（含标记上报 type=2）。
    func reportWithCoordinates(
        api: HomeAPI,
        latitude: Double?,
        longitude: Double?,
        address: String?,
        type: Int,
        showSuccessToast: Bool = true,
        desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest
    ) async throws -> LocationReportSuccessResult {
        if type != 3 {
            let now = currentTimestampMillis()
            if now - lastLocationReportTime < Self.operationIntervalMillis {
                let remaining = Int((Self.operationIntervalMillis - (now - lastLocationReportTime)) / 1000)
                throw LocationReportServiceError.tooFrequent(remainingSeconds: remaining)
            }
        }

        guard appStore.session.currentScenicId > 0 else {
            throw LocationReportServiceError.missingScenic
        }
        let staffId = appStore.session.userId.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !staffId.isEmpty else {
            throw LocationReportServiceError.missingStaffId
        }

        isReporting = true
        defer { isReporting = false }

        let snapshot: HomeLocationSnapshot
        if let latitude, let longitude {
            let resolvedAddress: String
            if let address, !address.isEmpty {
                resolvedAddress = address
            } else {
                resolvedAddress = await locationProvider.reverseGeocode(
                    latitude: latitude,
                    longitude: longitude
                )
            }
            snapshot = HomeLocationSnapshot(
                latitude: latitude,
                longitude: longitude,
                address: resolvedAddress
            )
        } else {
            snapshot = try await locationProvider.requestSnapshot(desiredAccuracy: desiredAccuracy)
        }

        let locationText = snapshot.address.isEmpty
            ? String(format: "%.5f, %.5f", snapshot.latitude, snapshot.longitude)
            : snapshot.address

        _ = try await api.reportLocation(
            staffId: staffId,
            latitude: snapshot.latitude,
            longitude: snapshot.longitude,
            address: snapshot.address,
            type: type,
            scenicId: String(appStore.session.currentScenicId)
        )

        lastLocationReportTime = currentTimestampMillis()
        if type != 3 {
            locationStateStore.startLocationReport()
            let reportTime = formattedReportTime()
            if showSuccessToast {
                // Toast handled by caller via onShowMessage if needed
            }
            return LocationReportSuccessResult(
                reportTimeText: reportTime,
                locationInfoText: locationText,
                shouldShowSuccessDialog: true
            )
        }

        return LocationReportSuccessResult(
            reportTimeText: "",
            locationInfoText: locationText,
            shouldShowSuccessDialog: false
        )
    }

    private func formattedReportTime() -> String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "HH:mm"
        return formatter.string(from: Date())
    }

    private func currentTimestampMillis() -> Int64 {
        Int64(Date().timeIntervalSince1970 * 1000)
    }
}

/// 位置上报业务错误。
enum LocationReportServiceError: LocalizedError, Equatable {
    case tooFrequent(remainingSeconds: Int)
    case missingScenic
    case missingStaffId
    case locating

    var errorDescription: String? {
        switch self {
        case .tooFrequent(let remaining):
            "操作过于频繁，请\(remaining)秒后再试"
        case .missingScenic:
            "请先选择景区"
        case .missingStaffId:
            "获取用户ID失败"
        case .locating:
            "正在定位中，请稍候"
        }
    }
}
