//
//  AppleMapsLauncher.swift
//  SportsTime
//
//  Service for opening trip routes in Apple Maps
//

import Foundation
import MapKit
import CoreLocation

/// Result of preparing waypoints for Apple Maps
enum AppleMapsLaunchResult {
    /// Single route can be opened directly (≤16 waypoints)
    case ready([MKMapItem])

    /// Multiple routes needed (>16 waypoints)
    case multipleRoutes([[MKMapItem]])

    /// No routable waypoints available
    case noWaypoints
}

/// Handles opening trip routes in Apple Maps with chunking for large trips
struct AppleMapsLauncher {

    /// Maximum waypoints Apple Maps supports in a single route
    static let maxWaypoints = 16

    /// Represents a waypoint for the route
    struct Waypoint {
        let name: String
        let coordinate: CLLocationCoordinate2D
        let sortKey: (day: Int, order: Double)
    }

    // MARK: - Public API

    /// Prepares waypoints from trip stops and custom items
    /// - Parameters:
    ///   - stops: Trip stops with optional coordinates
    ///   - customItems: Custom itinerary items that may have coordinates
    /// - Returns: Launch result indicating if route is ready or needs chunking
    static func prepare(
        stops: [TripStop],
        customItems: [ItineraryItem]
    ) -> AppleMapsLaunchResult {
        let waypoints = collectWaypoints(stops: stops, customItems: customItems)

        guard !waypoints.isEmpty else {
            return .noWaypoints
        }

        let mapItems = waypoints.map { waypoint -> MKMapItem in
            let placemark = MKPlacemark(coordinate: waypoint.coordinate)
            let item = MKMapItem(placemark: placemark)
            item.name = waypoint.name
            return item
        }

        if mapItems.count <= maxWaypoints {
            return .ready(mapItems)
        } else {
            let chunks = chunk(mapItems, size: maxWaypoints)
            return .multipleRoutes(chunks)
        }
    }

    /// Opens a single route in Apple Maps
    /// - Parameter mapItems: Array of map items (≤16)
    static func open(_ mapItems: [MKMapItem]) {
        guard !mapItems.isEmpty else { return }

        MKMapItem.openMaps(with: mapItems, launchOptions: [
            MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
        ])
    }

    /// Opens a specific chunk of a multi-route trip
    /// - Parameters:
    ///   - chunks: All route chunks
    ///   - index: Which chunk to open (0-based)
    static func open(chunk index: Int, of chunks: [[MKMapItem]]) {
        guard index >= 0 && index < chunks.count else { return }
        open(chunks[index])
    }

    /// Opens a single location in Apple Maps
    /// - Parameters:
    ///   - coordinate: Location to display
    ///   - name: Display name for the pin
    static func openLocation(coordinate: CLLocationCoordinate2D, name: String) {
        let placemark = MKPlacemark(coordinate: coordinate)
        let mapItem = MKMapItem(placemark: placemark)
        mapItem.name = name
        mapItem.openInMaps(launchOptions: nil)
    }

    /// Opens driving directions between two points in Apple Maps
    /// - Parameters:
    ///   - from: Starting location
    ///   - fromName: Display name for origin
    ///   - to: Destination location
    ///   - toName: Display name for destination
    static func openDirections(
        from: CLLocationCoordinate2D,
        fromName: String,
        to: CLLocationCoordinate2D,
        toName: String
    ) {
        let fromPlacemark = MKPlacemark(coordinate: from)
        let fromItem = MKMapItem(placemark: fromPlacemark)
        fromItem.name = fromName

        let toPlacemark = MKPlacemark(coordinate: to)
        let toItem = MKMapItem(placemark: toPlacemark)
        toItem.name = toName

        MKMapItem.openMaps(with: [fromItem, toItem], launchOptions: [
            MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
        ])
    }

    // MARK: - Private Helpers

    private static func collectWaypoints(
        stops: [TripStop],
        customItems: [ItineraryItem]
    ) -> [Waypoint] {
        var waypoints: [Waypoint] = []

        // Add trip stops with coordinates
        for (index, stop) in stops.enumerated() {
            guard let coordinate = stop.coordinate else { continue }

            waypoints.append(Waypoint(
                name: stop.city,
                coordinate: coordinate,
                // Use stop number for day approximation, index for order
                sortKey: (day: stop.stopNumber, order: Double(index))
            ))
        }

        // Add mappable custom items
        for item in customItems {
            guard let info = item.customInfo,
                  let coordinate = info.coordinate else { continue }

            waypoints.append(Waypoint(
                name: info.title,
                coordinate: coordinate,
                sortKey: (day: item.day, order: item.sortOrder)
            ))
        }

        // Sort by day, then by sort order within day
        waypoints.sort { lhs, rhs in
            if lhs.sortKey.day != rhs.sortKey.day {
                return lhs.sortKey.day < rhs.sortKey.day
            }
            return lhs.sortKey.order < rhs.sortKey.order
        }

        return waypoints
    }

    private static func chunk(_ items: [MKMapItem], size: Int) -> [[MKMapItem]] {
        stride(from: 0, to: items.count, by: size).map { start in
            Array(items[start..<min(start + size, items.count)])
        }
    }
}
