//
//  ScenicSelectionModels.swift
//  suixinkan
//

import Foundation

/// 景区列表展示标签，对齐 Android `openStatus`。
enum ScenicSelectionOpenStatus: Equatable {
    case nearest
    case closed
    case none
}

/// 景区选择列表项，对齐 Android `Sceniclistv2Entity` 展示字段。
struct ScenicSpotDisplayItem: Equatable, Hashable {
    let id: Int
    let name: String
    let coverImg: String
    let address: String
    let distanceText: String
    let openStatus: ScenicSelectionOpenStatus
    let isSelected: Bool

    var statusLabel: String? {
        switch openStatus {
        case .nearest: "距离最近"
        case .closed: "暂未营业"
        case .none: nil
        }
    }
}

/// 景区距离计算，对齐 Android Haversine 逻辑。
enum ScenicDistanceCalculator {
    private static let earthRadiusMeters = 6_371_000.0

    static func distanceKilometers(
        fromLat: Double,
        fromLng: Double,
        toLat: Double,
        toLng: Double
    ) -> Double {
        let dLat = (toLat - fromLat) * .pi / 180
        let dLng = (toLng - fromLng) * .pi / 180
        let lat1 = fromLat * .pi / 180
        let lat2 = toLat * .pi / 180
        let a = sin(dLat / 2) * sin(dLat / 2)
            + cos(lat1) * cos(lat2) * sin(dLng / 2) * sin(dLng / 2)
        let c = 2 * atan2(sqrt(a), sqrt(1 - a))
        return (earthRadiusMeters * c) / 1000
    }

    static func formatDistanceKilometers(_ kilometers: Double) -> String {
        if kilometers <= 0 || !kilometers.isFinite {
            return "0"
        }
        return String(format: "%.2f", kilometers)
    }
}

/// 将 role-permission 景区映射为选择页展示项。
enum ScenicSpotDisplayMapper {
    static func buildItems(
        scenicList: [ScenicInfo],
        searchQuery: String,
        currentLat: Double?,
        currentLng: Double?,
        selectedScenicId: Int
    ) -> [ScenicSpotDisplayItem] {
        let hasLocation = (currentLat ?? 0) > 0 && (currentLng ?? 0) > 0
        let searchLower = searchQuery.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()

        struct ScenicWithDistance {
            let item: ScenicSpotDisplayItem
            let distance: Double
        }

        let mapped = scenicList.map { scenic -> ScenicWithDistance in
            let loc = scenic.location
            let distance: Double
            if let loc, hasLocation, let currentLat, let currentLng {
                distance = ScenicDistanceCalculator.distanceKilometers(
                    fromLat: currentLat,
                    fromLng: currentLng,
                    toLat: loc.lat,
                    toLng: loc.lng
                )
            } else if loc == nil, hasLocation {
                distance = .greatestFiniteMagnitude
            } else {
                distance = 0
            }

            let address = loc?.address.isEmpty == false ? (loc?.address ?? "--") : "--"
            let distanceText: String
            if loc == nil {
                distanceText = "--"
            } else if distance > 0, distance < .greatestFiniteMagnitude {
                distanceText = ScenicDistanceCalculator.formatDistanceKilometers(distance)
            } else {
                distanceText = "0"
            }

            let openStatus: ScenicSelectionOpenStatus = scenic.status == 2 ? .closed : .none
            let item = ScenicSpotDisplayItem(
                id: scenic.id,
                name: scenic.name,
                coverImg: scenic.coverImg ?? "",
                address: address,
                distanceText: distanceText,
                openStatus: openStatus,
                isSelected: selectedScenicId > 0 && scenic.id == selectedScenicId
            )
            return ScenicWithDistance(item: item, distance: distance)
        }

        let filtered = mapped.filter { entry in
            guard !searchLower.isEmpty else { return true }
            return entry.item.name.lowercased().contains(searchLower)
                || entry.item.address.lowercased().contains(searchLower)
        }

        let sorted: [ScenicWithDistance]
        if hasLocation {
            sorted = filtered.sorted { $0.distance < $1.distance }
        } else {
            sorted = filtered
        }

        let baseItems = sorted.map(\.item)
        guard hasLocation, !baseItems.isEmpty else { return baseItems }

        return baseItems.enumerated().map { index, item in
            if index == 0, item.openStatus != .closed {
                return ScenicSpotDisplayItem(
                    id: item.id,
                    name: item.name,
                    coverImg: item.coverImg,
                    address: item.address,
                    distanceText: item.distanceText,
                    openStatus: .nearest,
                    isSelected: item.isSelected
                )
            }
            return item
        }
    }
}
