//
//  PhotoMetadataExtractor.swift
//  SportsTime
//
//  Service for extracting EXIF metadata (GPS, date) from photos.
//

import Foundation
import Photos
import CoreLocation
import ImageIO
import UIKit

// MARK: - Photo Metadata

struct PhotoMetadata: Sendable {
    let captureDate: Date?
    let coordinates: CLLocationCoordinate2D?
    let hasValidLocation: Bool
    let hasValidDate: Bool

    nonisolated init(captureDate: Date?, coordinates: CLLocationCoordinate2D?) {
        self.captureDate = captureDate
        self.coordinates = coordinates
        self.hasValidLocation = coordinates != nil
        self.hasValidDate = captureDate != nil
    }

    nonisolated static var empty: PhotoMetadata {
        PhotoMetadata(captureDate: nil, coordinates: nil)
    }
}

// MARK: - Photo Metadata Extractor

actor PhotoMetadataExtractor {
    static let shared = PhotoMetadataExtractor()

    private init() {}

    // MARK: - PHAsset Extraction (Preferred)

    /// Extract metadata from PHAsset (preferred method)
    /// Uses PHAsset's location and creationDate properties
    func extractMetadata(from asset: PHAsset) async -> PhotoMetadata {
        // PHAsset provides location and date directly
        let coordinates: CLLocationCoordinate2D?
        if let location = asset.location {
            coordinates = location.coordinate
        } else {
            coordinates = nil
        }

        return PhotoMetadata(
            captureDate: asset.creationDate,
            coordinates: coordinates
        )
    }

    /// Extract metadata from multiple PHAssets
    func extractMetadata(from assets: [PHAsset]) async -> [PhotoMetadata] {
        var results: [PhotoMetadata] = []
        for asset in assets {
            let metadata = await extractMetadata(from: asset)
            results.append(metadata)
        }
        return results
    }

    // MARK: - Image Data Extraction (Fallback)

    /// Extract metadata from raw image data using ImageIO
    /// Useful when PHAsset is not available
    func extractMetadata(from imageData: Data) -> PhotoMetadata {
        guard let source = CGImageSourceCreateWithData(imageData as CFData, nil),
              let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any] else {
            return .empty
        }

        let captureDate = extractDate(from: properties)
        let coordinates = extractCoordinates(from: properties)

        return PhotoMetadata(
            captureDate: captureDate,
            coordinates: coordinates
        )
    }

    // MARK: - Private Helpers

    private func extractDate(from properties: [CFString: Any]) -> Date? {
        // Try EXIF DateTimeOriginal first
        if let exif = properties[kCGImagePropertyExifDictionary] as? [CFString: Any],
           let dateString = exif[kCGImagePropertyExifDateTimeOriginal] as? String {
            return parseExifDate(dateString)
        }

        // Try TIFF DateTime
        if let tiff = properties[kCGImagePropertyTIFFDictionary] as? [CFString: Any],
           let dateString = tiff[kCGImagePropertyTIFFDateTime] as? String {
            return parseExifDate(dateString)
        }

        return nil
    }

    private func extractCoordinates(from properties: [CFString: Any]) -> CLLocationCoordinate2D? {
        guard let gps = properties[kCGImagePropertyGPSDictionary] as? [CFString: Any],
              let latitude = gps[kCGImagePropertyGPSLatitude] as? Double,
              let longitude = gps[kCGImagePropertyGPSLongitude] as? Double else {
            return nil
        }

        // Handle N/S and E/W references
        var lat = latitude
        var lon = longitude

        if let latRef = gps[kCGImagePropertyGPSLatitudeRef] as? String, latRef == "S" {
            lat = -lat
        }

        if let lonRef = gps[kCGImagePropertyGPSLongitudeRef] as? String, lonRef == "W" {
            lon = -lon
        }

        // Validate coordinates
        guard lat >= -90 && lat <= 90 && lon >= -180 && lon <= 180 else {
            return nil
        }

        return CLLocationCoordinate2D(latitude: lat, longitude: lon)
    }

    private func parseExifDate(_ dateString: String) -> Date? {
        // EXIF date format: "2024:06:15 14:30:00"
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy:MM:dd HH:mm:ss"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter.date(from: dateString)
    }
}

// MARK: - Photo Library Access

extension PhotoMetadataExtractor {
    /// Request photo library access
    @MainActor
    func requestPhotoLibraryAccess() async -> PHAuthorizationStatus {
        let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)

        switch status {
        case .notDetermined:
            return await PHPhotoLibrary.requestAuthorization(for: .readWrite)
        default:
            return status
        }
    }

    /// Check if photo library access is available
    var hasPhotoLibraryAccess: Bool {
        let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
        return status == .authorized || status == .limited
    }
}

// MARK: - Asset Image Loading

extension PhotoMetadataExtractor {
    /// Load thumbnail image from PHAsset
    func loadThumbnail(from asset: PHAsset, targetSize: CGSize = CGSize(width: 200, height: 200)) async -> UIImage? {
        await withCheckedContinuation { continuation in
            let options = PHImageRequestOptions()
            options.deliveryMode = .fastFormat
            options.resizeMode = .fast
            options.isSynchronous = false

            PHImageManager.default().requestImage(
                for: asset,
                targetSize: targetSize,
                contentMode: .aspectFill,
                options: options
            ) { image, _ in
                continuation.resume(returning: image)
            }
        }
    }

    /// Load full-size image data from PHAsset
    func loadImageData(from asset: PHAsset) async -> Data? {
        await withCheckedContinuation { continuation in
            let options = PHImageRequestOptions()
            options.deliveryMode = .highQualityFormat
            options.isSynchronous = false

            PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { data, _, _, _ in
                continuation.resume(returning: data)
            }
        }
    }
}
