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

import CoreLocation
import Foundation
import Observation

/// 景区选择展示实体，表示列表中的一个可切换景区。
struct ScenicSelectionItem: Equatable, Identifiable {
    let id: Int
    let name: String
    let status: Int?
    let address: String
    let latitude: Double?
    let longitude: Double?
    let coverURLString: String?
    var isClosest: Bool
    var distanceMeters: CLLocationDistance?

    /// 从业务作用域创建景区选择展示实体。
    init(scope: BusinessScope) {
        id = scope.id
        name = scope.name
        status = scope.status
        let trimmedAddress = scope.address?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        address = trimmedAddress.isEmpty ? "--" : trimmedAddress
        latitude = scope.latitude
        longitude = scope.longitude
        coverURLString = scope.coverURLString
        isClosest = false
        distanceMeters = nil
    }

    /// 返回景区距离文案，无定位结果时显示占位。
    var distanceText: String {
        guard let distanceMeters else { return "--" }
        if distanceMeters < 1_000 {
            return "\(Int(distanceMeters.rounded()))m"
        }
        return String(format: "%.2fkm", distanceMeters / 1_000)
    }

    /// 返回景区是否处于旧工程定义的暂未营业状态。
    var isClosed: Bool {
        status == 2
    }
}

@MainActor
@Observable
/// 景区选择 ViewModel，负责搜索、定位距离计算和切换景区持久化。
final class ScenicSelectionViewModel {
    var searchQuery = ""
    var currentLocationText = "定位后获取最近景区"
    var locationWarning: String?
    private(set) var items: [ScenicSelectionItem] = []

    /// 返回按搜索关键词过滤后的景区列表。
    var filteredItems: [ScenicSelectionItem] {
        let keyword = searchQuery.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !keyword.isEmpty else { return items }
        return items.filter { item in
            item.name.localizedCaseInsensitiveContains(keyword)
                || item.address.localizedCaseInsensitiveContains(keyword)
        }
    }

    /// 从账号上下文重新加载可选景区，并优先把当前景区放在第一位。
    func reload(from accountContext: AccountContext) {
        let currentScenicId = accountContext.currentScenic?.id
        items = Self.makeItems(scopes: accountContext.scenicScopes, currentScenicId: currentScenicId)
        currentLocationText = items.first?.address ?? "定位后获取最近景区"
        locationWarning = nil
    }

    /// 切换景区并把当前选择保存到账号快照。
    func select(
        scenicId: Int,
        accountContext: AccountContext,
        permissionContext: PermissionContext,
        snapshotStore: AccountSnapshotStore
    ) {
        accountContext.selectScenic(id: scenicId)
        snapshotStore.saveCurrentSelection(
            accountContext: accountContext,
            currentRoleId: permissionContext.currentRole?.id
        )
    }

    /// 定位成功后计算距离，并标记距离最近景区。
    func applyCurrentLocation(latitude: Double, longitude: Double) {
        locationWarning = nil
        let current = CLLocation(latitude: latitude, longitude: longitude)
        for index in items.indices {
            guard let scenicLatitude = items[index].latitude,
                  let scenicLongitude = items[index].longitude,
                  scenicLatitude != 0 || scenicLongitude != 0
            else {
                items[index].distanceMeters = nil
                items[index].isClosest = false
                continue
            }
            let scenicLocation = CLLocation(latitude: scenicLatitude, longitude: scenicLongitude)
            items[index].distanceMeters = scenicLocation.distance(from: current)
            items[index].isClosest = false
        }
        if let closestIndex = items.indices
            .filter({ items[$0].distanceMeters != nil })
            .min(by: { (items[$0].distanceMeters ?? .greatestFiniteMagnitude) < (items[$1].distanceMeters ?? .greatestFiniteMagnitude) }) {
            items[closestIndex].isClosest = true
            currentLocationText = items[closestIndex].address
        } else if let first = items.first {
            items[0].isClosest = true
            currentLocationText = first.address
        }
    }

    /// 定位失败时保留原列表并暴露提示文案。
    func applyLocationFailure(_ message: String) {
        locationWarning = message
        currentLocationText = items.first?.address ?? "定位后获取最近景区"
        if !items.isEmpty, !items.contains(where: \.isClosest) {
            items[0].isClosest = true
        }
    }

    /// 从景区作用域生成展示列表，并把当前景区移动到首位。
    static func makeItems(scopes: [BusinessScope], currentScenicId: Int?) -> [ScenicSelectionItem] {
        var seen = Set<Int>()
        let uniqueItems = scopes.compactMap { scope -> ScenicSelectionItem? in
            guard scope.kind == .scenic, seen.insert(scope.id).inserted else { return nil }
            return ScenicSelectionItem(scope: scope)
        }
        var result: [ScenicSelectionItem]
        if let currentScenicId,
           let current = uniqueItems.first(where: { $0.id == currentScenicId }) {
            result = [current] + uniqueItems.filter { $0.id != currentScenicId }
        } else {
            result = uniqueItems
        }
        if !result.isEmpty {
            result[0].isClosest = true
        }
        return result
    }
}
