//
//  EVChargingService.swift
//  SportsTime
//

import Foundation
import CoreLocation
import MapKit

actor EVChargingService {
    static let shared = EVChargingService()

    private init() {}

    // MARK: - Public API

    /// Find EV chargers along a route polyline
    /// - Parameters:
    ///   - polyline: The route polyline from MKDirections
    ///   - searchRadiusMiles: Radius to search around each sample point (default 5 miles)
    ///   - intervalMiles: Distance between sample points along route (default 100 miles)
    /// - Returns: Array of EV charging stops along the route, deduplicated and sorted by distance from start
    func findChargersAlongRoute(
        polyline: MKPolyline,
        searchRadiusMiles: Double = 5.0,
        intervalMiles: Double = 100.0
    ) async throws -> [EVChargingStop] {
        let samplePoints = samplePoints(from: polyline, intervalMiles: intervalMiles)

        guard !samplePoints.isEmpty else { return [] }

        var allChargers: [EVChargingStop] = []
        var seenIds = Set<String>()  // For deduplication by name+coordinate

        for point in samplePoints {
            do {
                let mapItems = try await searchChargers(near: point, radiusMiles: searchRadiusMiles)

                for item in mapItems {
                    let charger = mapItemToChargingStop(item)
                    let uniqueKey = "\(charger.name)-\(charger.location.coordinate?.latitude ?? 0)"

                    if !seenIds.contains(uniqueKey) {
                        seenIds.insert(uniqueKey)
                        allChargers.append(charger)
                    }
                }
            } catch {
                // Continue with other points if one search fails
                continue
            }
        }

        return allChargers
    }

    // MARK: - Private Helpers

    /// Sample points along a polyline at given intervals
    private func samplePoints(
        from polyline: MKPolyline,
        intervalMiles: Double
    ) -> [CLLocationCoordinate2D] {
        let pointCount = polyline.pointCount
        guard pointCount > 1 else { return [] }

        let points = polyline.points()
        var samples: [CLLocationCoordinate2D] = []
        var accumulatedDistance: Double = 0
        let intervalMeters = intervalMiles * 1609.34  // Convert miles to meters

        // Always include start point
        samples.append(points[0].coordinate)

        for i in 1..<pointCount {
            let prev = CLLocation(
                latitude: points[i - 1].coordinate.latitude,
                longitude: points[i - 1].coordinate.longitude
            )
            let current = CLLocation(
                latitude: points[i].coordinate.latitude,
                longitude: points[i].coordinate.longitude
            )

            accumulatedDistance += current.distance(from: prev)

            if accumulatedDistance >= intervalMeters {
                samples.append(points[i].coordinate)
                accumulatedDistance = 0
            }
        }

        // Always include end point if different from last sample
        let lastPoint = points[pointCount - 1].coordinate
        if let lastSample = samples.last {
            let distance = CLLocation(latitude: lastPoint.latitude, longitude: lastPoint.longitude)
                .distance(from: CLLocation(latitude: lastSample.latitude, longitude: lastSample.longitude))
            if distance > 1000 {  // More than 1km away
                samples.append(lastPoint)
            }
        }

        return samples
    }

    /// Search for EV chargers near a coordinate
    private func searchChargers(
        near coordinate: CLLocationCoordinate2D,
        radiusMiles: Double
    ) async throws -> [MKMapItem] {
        let radiusMeters = radiusMiles * 1609.34

        let request = MKLocalPointsOfInterestRequest(
            center: coordinate,
            radius: radiusMeters
        )
        request.pointOfInterestFilter = MKPointOfInterestFilter(including: [.evCharger])

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

        return response.mapItems
    }

    /// Convert an MKMapItem to an EVChargingStop
    private func mapItemToChargingStop(_ item: MKMapItem) -> EVChargingStop {
        let name = item.name ?? "EV Charger"
        let chargerType = detectChargerType(from: name)
        let estimatedChargeTime = estimateChargeTime(for: chargerType)

        var address: String? = nil
        if let placemark = item.placemark as MKPlacemark? {
            var components: [String] = []
            if let city = placemark.locality { components.append(city) }
            if let state = placemark.administrativeArea { components.append(state) }
            address = components.isEmpty ? nil : components.joined(separator: ", ")
        }

        return EVChargingStop(
            name: name,
            location: LocationInput(
                name: name,
                coordinate: item.placemark.coordinate,
                address: address
            ),
            chargerType: chargerType,
            estimatedChargeTime: estimatedChargeTime
        )
    }

    /// Detect charger type from name using heuristics
    private func detectChargerType(from name: String) -> ChargerType {
        let lowercased = name.lowercased()

        if lowercased.contains("tesla") || lowercased.contains("supercharger") {
            return .supercharger
        }

        if lowercased.contains("dc fast") ||
           lowercased.contains("dcfc") ||
           lowercased.contains("ccs") ||
           lowercased.contains("chademo") ||
           lowercased.contains("electrify america") ||
           lowercased.contains("evgo") {
            return .dcFast
        }

        // Default to Level 2 for ChargePoint, Blink, and others
        return .level2
    }

    /// Estimate charge time based on charger type
    private func estimateChargeTime(for chargerType: ChargerType) -> TimeInterval {
        switch chargerType {
        case .supercharger:
            return 25 * 60  // 25 minutes
        case .dcFast:
            return 30 * 60  // 30 minutes
        case .level2:
            return 120 * 60  // 2 hours
        }
    }
}
