//
//  POISearchService.swift
//  SportsTime
//
//  Searches for nearby points of interest using MapKit for PDF city spotlights.
//

import Foundation
import MapKit
import CoreLocation

actor POISearchService {

    // MARK: - Types

    struct POI: Identifiable, Hashable {
        let id: UUID
        let name: String
        let category: POICategory
        let coordinate: CLLocationCoordinate2D
        let distanceMeters: Double
        let address: String?

        var formattedDistance: String {
            let miles = distanceMeters * 0.000621371
            if miles < 0.1 {
                let feet = distanceMeters * 3.28084
                return String(format: "%.0f ft", feet)
            } else {
                return String(format: "%.1f mi", miles)
            }
        }

        // Hashable conformance for CLLocationCoordinate2D
        func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }

        static func == (lhs: POI, rhs: POI) -> Bool {
            lhs.id == rhs.id
        }
    }

    enum POICategory: String, CaseIterable {
        case restaurant
        case attraction
        case entertainment
        case nightlife
        case museum

        var displayName: String {
            switch self {
            case .restaurant: return "Restaurant"
            case .attraction: return "Attraction"
            case .entertainment: return "Entertainment"
            case .nightlife: return "Nightlife"
            case .museum: return "Museum"
            }
        }

        var iconName: String {
            switch self {
            case .restaurant: return "fork.knife"
            case .attraction: return "star.fill"
            case .entertainment: return "theatermasks.fill"
            case .nightlife: return "moon.stars.fill"
            case .museum: return "building.columns.fill"
            }
        }

        var mkPointOfInterestCategory: MKPointOfInterestCategory {
            switch self {
            case .restaurant: return .restaurant
            case .attraction: return .nationalPark
            case .entertainment: return .theater
            case .nightlife: return .nightlife
            case .museum: return .museum
            }
        }

        var searchQuery: String {
            switch self {
            case .restaurant: return "restaurants"
            case .attraction: return "tourist attractions"
            case .entertainment: return "entertainment"
            case .nightlife: return "bars nightlife"
            case .museum: return "museums"
            }
        }
    }

    // MARK: - Errors

    enum POISearchError: Error, LocalizedError {
        case searchFailed(String)
        case noResults

        var errorDescription: String? {
            switch self {
            case .searchFailed(let reason):
                return "POI search failed: \(reason)"
            case .noResults:
                return "No points of interest found"
            }
        }
    }

    // MARK: - Public Methods

    /// Find nearby POIs for a city/stadium location
    func findNearbyPOIs(
        near coordinate: CLLocationCoordinate2D,
        categories: [POICategory] = [.restaurant, .attraction, .entertainment],
        radiusMeters: Double = 3000,
        limitPerCategory: Int = 2
    ) async throws -> [POI] {
        var allPOIs: [POI] = []

        // Search each category in parallel
        await withTaskGroup(of: [POI].self) { group in
            for category in categories {
                group.addTask {
                    do {
                        return try await self.searchCategory(
                            category,
                            near: coordinate,
                            radiusMeters: radiusMeters,
                            limit: limitPerCategory
                        )
                    } catch {
                        return []
                    }
                }
            }

            for await pois in group {
                allPOIs.append(contentsOf: pois)
            }
        }

        // Sort by distance
        allPOIs.sort { $0.distanceMeters < $1.distanceMeters }

        return allPOIs
    }

    /// Find POIs for multiple cities (one search per city)
    func findPOIsForCities(
        stops: [TripStop],
        categories: [POICategory] = [.restaurant, .attraction, .entertainment],
        limit: Int = 5
    ) async -> [String: [POI]] {
        var results: [String: [POI]] = [:]

        await withTaskGroup(of: (String, [POI]).self) { group in
            for stop in stops {
                guard let coordinate = stop.coordinate else { continue }

                group.addTask {
                    do {
                        let pois = try await self.findNearbyPOIs(
                            near: coordinate,
                            categories: categories,
                            limitPerCategory: 2
                        )
                        // Take top N overall
                        return (stop.city, Array(pois.prefix(limit)))
                    } catch {
                        return (stop.city, [])
                    }
                }
            }

            for await (city, pois) in group {
                if !pois.isEmpty {
                    results[city] = pois
                }
            }
        }

        return results
    }

    // MARK: - Private Methods

    private func searchCategory(
        _ category: POICategory,
        near coordinate: CLLocationCoordinate2D,
        radiusMeters: Double,
        limit: Int
    ) async throws -> [POI] {
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = category.searchQuery
        request.region = MKCoordinateRegion(
            center: coordinate,
            latitudinalMeters: radiusMeters * 2,
            longitudinalMeters: radiusMeters * 2
        )
        request.resultTypes = .pointOfInterest

        let search = MKLocalSearch(request: request)
        let response = try await search.start()

        let referenceLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)

        let pois: [POI] = response.mapItems.prefix(limit).compactMap { item in
            guard let name = item.name else { return nil }

            let itemCoordinate = item.location.coordinate
            let distance = referenceLocation.distance(from: item.location)

            // Only include POIs within radius
            guard distance <= radiusMeters else { return nil }

            return POI(
                id: UUID(),
                name: name,
                category: category,
                coordinate: itemCoordinate,
                distanceMeters: distance,
                address: formatAddress(item)
            )
        }

        return pois
    }

    @available(iOS, deprecated: 26.0, message: "Uses placemark for address formatting")
    private func formatAddress(_ item: MKMapItem) -> String? {
        var components: [String] = []

        if let subThoroughfare = item.placemark.subThoroughfare {
            components.append(subThoroughfare)
        }
        if let thoroughfare = item.placemark.thoroughfare {
            components.append(thoroughfare)
        }

        guard !components.isEmpty else { return nil }
        return components.joined(separator: " ")
    }
}
