import Foundation

/// Pulls airframe metadata (manufacturer build date, first-flight date)
/// from OpenSky's `/api/metadata/aircraft/icao/{icao24}` endpoint and
/// caches the result in `AirframeMetadata`. Cleaner than scraping
/// jetphotos / planespotters airframe pages — both of those sit behind
/// Cloudflare's bot gate and aren't reliably fetchable from a mobile
/// client.
///
/// Caveat: OpenSky's metadata is community-contributed and often null
/// for newer airframes. We degrade gracefully — no date means we just
/// don't show an age in the detail view.
actor AirframeMetadataService {
    static let shared = AirframeMetadataService()

    struct Metadata: Hashable, Sendable {
        let registration: String
        let built: Date?
        let firstFlightDate: Date?
    }

    private let session: URLSession
    private var inflight: [String: Task<Metadata?, Never>] = [:]

    init(session: URLSession = .shared) {
        self.session = session
    }

    /// Look up metadata for an aircraft by ICAO24 hex. Coalesces
    /// concurrent requests for the same icao24 so we never fire twice.
    /// Returns nil on network error / no record.
    func metadata(forICAO24 icao24: String) async -> Metadata? {
        let key = icao24.lowercased()
        if let inflight = inflight[key] {
            return await inflight.value
        }
        let task = Task<Metadata?, Never> { [weak self] in
            guard let self else { return nil }
            return await self.fetch(icao24: key)
        }
        inflight[key] = task
        let result = await task.value
        inflight.removeValue(forKey: key)
        return result
    }

    private func fetch(icao24: String) async -> Metadata? {
        guard let url = URL(string: "https://opensky-network.org/api/metadata/aircraft/icao/\(icao24)") else {
            return nil
        }
        var req = URLRequest(url: url)
        req.timeoutInterval = 12
        req.setValue("application/json", forHTTPHeaderField: "Accept")

        do {
            let (data, resp) = try await session.data(for: req)
            guard let http = resp as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {
                return nil
            }
            guard let root = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                return nil
            }
            let registration = root["registration"] as? String ?? ""
            let built = parseDate(root["built"] as? String)
            let firstFlight = parseDate(root["firstFlightDate"] as? String)
            return Metadata(
                registration: registration,
                built: built,
                firstFlightDate: firstFlight
            )
        } catch {
            return nil
        }
    }

    /// OpenSky returns dates as "YYYY-MM-DD" strings.
    private func parseDate(_ s: String?) -> Date? {
        guard let s, !s.isEmpty else { return nil }
        let f = DateFormatter()
        f.dateFormat = "yyyy-MM-dd"
        f.timeZone = TimeZone(identifier: "UTC")
        return f.date(from: s)
    }
}
