//
//  MapSnapshotService.swift
//  SportsTime
//
//  Generates static map images for PDF export using MKMapSnapshotter.
//

import Foundation
import MapKit
import UIKit

actor MapSnapshotService {

    // MARK: - Errors

    enum MapSnapshotError: Error, LocalizedError {
        case noStops
        case snapshotFailed(String)
        case invalidCoordinates

        var errorDescription: String? {
            switch self {
            case .noStops:
                return "No stops provided for map generation"
            case .snapshotFailed(let reason):
                return "Map snapshot failed: \(reason)"
            case .invalidCoordinates:
                return "Invalid coordinates for map region"
            }
        }
    }

    // MARK: - Route Map

    /// Generate a full route map showing all stops with route line
    func generateRouteMap(
        stops: [TripStop],
        size: CGSize,
        routeColor: UIColor = UIColor(red: 0.12, green: 0.23, blue: 0.54, alpha: 1.0)
    ) async throws -> UIImage {
        guard !stops.isEmpty else {
            throw MapSnapshotError.noStops
        }

        let coordinates = stops.compactMap { $0.coordinate }
        guard !coordinates.isEmpty else {
            throw MapSnapshotError.invalidCoordinates
        }

        // Calculate region to fit all stops with padding
        let region = regionToFit(coordinates: coordinates, paddingPercent: 0.2)

        // Configure snapshotter
        let options = MKMapSnapshotter.Options()
        options.region = region
        options.size = size
        options.mapType = .standard
        options.showsBuildings = false
        options.pointOfInterestFilter = .excludingAll

        // Generate snapshot
        let snapshotter = MKMapSnapshotter(options: options)
        let snapshot = try await snapshotter.start()

        // Draw route and markers on snapshot
        let image = drawRouteOverlay(
            on: snapshot,
            coordinates: coordinates,
            stops: stops,
            routeColor: routeColor,
            size: size
        )

        return image
    }

    // MARK: - City Map

    /// Generate a city-level map showing stadium location
    func generateCityMap(
        stop: TripStop,
        size: CGSize,
        radiusMeters: Double = 5000
    ) async throws -> UIImage {
        guard let coordinate = stop.coordinate else {
            throw MapSnapshotError.invalidCoordinates
        }

        // Create region centered on stadium
        let region = MKCoordinateRegion(
            center: coordinate,
            latitudinalMeters: radiusMeters * 2,
            longitudinalMeters: radiusMeters * 2
        )

        // Configure snapshotter
        let options = MKMapSnapshotter.Options()
        options.region = region
        options.size = size
        options.mapType = .standard
        options.showsBuildings = true
        options.pointOfInterestFilter = .includingAll

        // Generate snapshot
        let snapshotter = MKMapSnapshotter(options: options)
        let snapshot = try await snapshotter.start()

        // Draw stadium marker
        let image = drawStadiumMarker(
            on: snapshot,
            coordinate: coordinate,
            cityName: stop.city,
            size: size
        )

        return image
    }

    // MARK: - Helper Methods

    /// Calculate a region that fits all coordinates with padding
    private func regionToFit(coordinates: [CLLocationCoordinate2D], paddingPercent: Double) -> MKCoordinateRegion {
        var minLat = coordinates[0].latitude
        var maxLat = coordinates[0].latitude
        var minLon = coordinates[0].longitude
        var maxLon = coordinates[0].longitude

        for coord in coordinates {
            minLat = min(minLat, coord.latitude)
            maxLat = max(maxLat, coord.latitude)
            minLon = min(minLon, coord.longitude)
            maxLon = max(maxLon, coord.longitude)
        }

        let latSpan = (maxLat - minLat) * (1 + paddingPercent)
        let lonSpan = (maxLon - minLon) * (1 + paddingPercent)

        let center = CLLocationCoordinate2D(
            latitude: (minLat + maxLat) / 2,
            longitude: (minLon + maxLon) / 2
        )

        let span = MKCoordinateSpan(
            latitudeDelta: max(latSpan, 0.5),
            longitudeDelta: max(lonSpan, 0.5)
        )

        return MKCoordinateRegion(center: center, span: span)
    }

    /// Draw route line and numbered markers on snapshot
    private func drawRouteOverlay(
        on snapshot: MKMapSnapshotter.Snapshot,
        coordinates: [CLLocationCoordinate2D],
        stops: [TripStop],
        routeColor: UIColor,
        size: CGSize
    ) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        defer { UIGraphicsEndImageContext() }

        // Draw base map
        snapshot.image.draw(at: .zero)

        guard let context = UIGraphicsGetCurrentContext() else {
            return snapshot.image
        }

        // Draw route line connecting stops
        if coordinates.count > 1 {
            context.setStrokeColor(routeColor.cgColor)
            context.setLineWidth(3.0)
            context.setLineCap(.round)
            context.setLineJoin(.round)

            let points = coordinates.map { snapshot.point(for: $0) }
            context.move(to: points[0])
            for i in 1..<points.count {
                context.addLine(to: points[i])
            }
            context.strokePath()
        }

        // Draw numbered markers for each stop
        for (index, coordinate) in coordinates.enumerated() {
            let point = snapshot.point(for: coordinate)
            drawNumberedMarker(
                at: point,
                number: index + 1,
                color: routeColor,
                in: context
            )
        }

        return UIGraphicsGetImageFromCurrentImageContext() ?? snapshot.image
    }

    /// Draw stadium marker on city map
    private func drawStadiumMarker(
        on snapshot: MKMapSnapshotter.Snapshot,
        coordinate: CLLocationCoordinate2D,
        cityName: String,
        size: CGSize
    ) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        defer { UIGraphicsEndImageContext() }

        // Draw base map
        snapshot.image.draw(at: .zero)

        guard let context = UIGraphicsGetCurrentContext() else {
            return snapshot.image
        }

        let point = snapshot.point(for: coordinate)

        // Draw stadium pin
        let pinSize: CGFloat = 30
        let pinRect = CGRect(
            x: point.x - pinSize / 2,
            y: point.y - pinSize,
            width: pinSize,
            height: pinSize
        )

        // Draw pin shadow
        context.saveGState()
        context.setShadow(offset: CGSize(width: 0, height: 2), blur: 4, color: UIColor.black.withAlphaComponent(0.3).cgColor)

        // Draw pin body (red marker)
        context.setFillColor(UIColor.systemRed.cgColor)
        context.fillEllipse(in: pinRect.insetBy(dx: 2, dy: 2))

        // Draw pin center (white dot)
        context.setFillColor(UIColor.white.cgColor)
        let centerRect = CGRect(
            x: point.x - 5,
            y: point.y - pinSize / 2 - 5,
            width: 10,
            height: 10
        )
        context.fillEllipse(in: centerRect)

        context.restoreGState()

        return UIGraphicsGetImageFromCurrentImageContext() ?? snapshot.image
    }

    /// Draw a numbered circular marker
    private func drawNumberedMarker(
        at point: CGPoint,
        number: Int,
        color: UIColor,
        in context: CGContext
    ) {
        let radius: CGFloat = 14
        let rect = CGRect(
            x: point.x - radius,
            y: point.y - radius,
            width: radius * 2,
            height: radius * 2
        )

        // Draw white outline
        context.saveGState()
        context.setShadow(offset: CGSize(width: 0, height: 1), blur: 2, color: UIColor.black.withAlphaComponent(0.3).cgColor)
        context.setFillColor(UIColor.white.cgColor)
        context.fillEllipse(in: rect.insetBy(dx: -2, dy: -2))
        context.restoreGState()

        // Draw colored circle
        context.setFillColor(color.cgColor)
        context.fillEllipse(in: rect)

        // Draw number text
        let numberString = "\(number)" as NSString
        let attributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.boldSystemFont(ofSize: 12),
            .foregroundColor: UIColor.white
        ]

        let textSize = numberString.size(withAttributes: attributes)
        let textRect = CGRect(
            x: point.x - textSize.width / 2,
            y: point.y - textSize.height / 2,
            width: textSize.width,
            height: textSize.height
        )
        numberString.draw(in: textRect, withAttributes: attributes)
    }
}
