import Foundation

/// Look up airline display info by ICAO callsign prefix.
///
/// Backed by `airlines.json` (bundled, ~2,700 entries sourced from
/// flightradar24.com's public `/mobile/airlines` feed) and falls back to
/// a small hardcoded map of the most common carriers if the bundle is
/// missing for any reason.
///
/// Lookup is by ICAO 3-letter code (e.g. "DAL" → Delta). IATA-only
/// lookups also work (e.g. "DL"). Anything that doesn't match either
/// returns the raw code as the name so the UI never blanks out.
final class AircraftRegistry: @unchecked Sendable {
    static let shared = AircraftRegistry()

    struct Entry: Sendable {
        let icao: String?      // 3-letter ICAO
        let iata: String?      // 2-3 char IATA
        let name: String       // display name
        let logoURL: URL?      // FR24 CDN URL, may be nil
    }

    private let lock = NSLock()
    private var byICAO: [String: Entry] = [:]
    private var byIATA: [String: Entry] = [:]
    private var isLoaded = false

    private init() {
        // Bootstrap with the hardcoded fallback so reads never come back
        // empty even before preload finishes.
        var icao: [String: Entry] = [:]
        var iata: [String: Entry] = [:]
        for (code, info) in Self.builtIn {
            let e = Entry(icao: code, iata: info.0, name: info.1, logoURL: nil)
            icao[code] = e
            iata[info.0] = e
        }
        byICAO = icao
        byIATA = iata
    }

    /// Kick off the airlines JSON parse on a background thread. Safe to
    /// call multiple times. Call at app launch so we never hit the parse
    /// cost when the user opens the Live tab.
    func preload() {
        lock.lock()
        if isLoaded {
            lock.unlock()
            return
        }
        isLoaded = true
        lock.unlock()

        Task.detached(priority: .utility) { [weak self] in
            guard let url = Bundle.main.url(forResource: "airlines", withExtension: "json"),
                  let data = try? Data(contentsOf: url),
                  let raw = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]]
            else { return }

            var icaoMap: [String: Entry] = [:]
            var iataMap: [String: Entry] = [:]
            for row in raw {
                let icao = (row["i"] as? String).map { $0.isEmpty ? nil : $0 } ?? nil
                let iata = (row["a"] as? String).map { $0.isEmpty ? nil : $0 } ?? nil
                let name = row["n"] as? String ?? ""
                let logo = (row["l"] as? String).flatMap(URL.init(string:))
                guard !name.isEmpty else { continue }
                let entry = Entry(icao: icao, iata: iata, name: name, logoURL: logo)
                if let icao { icaoMap[icao.uppercased()] = entry }
                if let iata { iataMap[iata.uppercased()] = entry }
            }
            self?.lock.withLock {
                self?.byICAO = icaoMap
                self?.byIATA = iataMap
            }
        }
    }

    /// Look up by ICAO 3-letter prefix (e.g. "DAL").
    func lookup(icao: String?) -> Entry? {
        guard let icao = icao?.uppercased(), !icao.isEmpty else { return nil }
        return byICAO[icao]
    }

    /// Look up by IATA 2-3 char code (e.g. "DL").
    func lookup(iata: String?) -> Entry? {
        guard let iata = iata?.uppercased(), !iata.isEmpty else { return nil }
        return byIATA[iata]
    }

    /// Convenience that returns a non-nil display name, falling back to
    /// the raw ICAO code or "Unknown".
    func displayName(icao: String?) -> String {
        if let e = lookup(icao: icao) { return e.name }
        return icao?.uppercased() ?? "Unknown"
    }

    /// Static fallback used when the bundled JSON isn't available.
    private static let builtIn: [String: (String, String)] = [
        "AAL": ("AA", "American Airlines"),
        "DAL": ("DL", "Delta Air Lines"),
        "UAL": ("UA", "United Airlines"),
        "SWA": ("WN", "Southwest Airlines"),
        "ASA": ("AS", "Alaska Airlines"),
        "JBU": ("B6", "JetBlue"),
        "SCX": ("SY", "Sun Country Airlines"),
        "HAL": ("HA", "Hawaiian Airlines"),
        "FFT": ("F9", "Frontier Airlines"),
        "AAY": ("G4", "Allegiant Air"),
        "AMX": ("AM", "Aeromexico"),
        "ACA": ("AC", "Air Canada"),
        "WJA": ("WS", "WestJet"),
        "UAE": ("EK", "Emirates"),
        "QTR": ("QR", "Qatar Airways"),
        "BAW": ("BA", "British Airways"),
        "DLH": ("LH", "Lufthansa"),
        "AFR": ("AF", "Air France"),
        "KLM": ("KL", "KLM"),
        "JAL": ("JL", "Japan Airlines"),
        "ANA": ("NH", "All Nippon Airways"),
        "KAL": ("KE", "Korean Air"),
        "CPA": ("CX", "Cathay Pacific"),
        "SIA": ("SQ", "Singapore Airlines"),
        "QFA": ("QF", "Qantas"),
        "FDX": ("FX", "FedEx"),
        "UPS": ("5X", "UPS Airlines")
    ]
}
