import AppKit
import Darwin
import Foundation
import WebKit

private func logSmoke(_ message: String) {
    fputs("[JSX-SMOKE] \(message)\n", stderr)
    fflush(stderr)
}

final class JSXSwiftSmokeApp: NSObject {
    private let origin: String
    private let destination: String
    private let date: String
    private let useService: Bool
    private let flightNumber: String
    private let departureTime: String?

    var exitCode: Int32 = 1

    init(
        origin: String,
        destination: String,
        date: String,
        useService: Bool,
        flightNumber: String,
        departureTime: String?
    ) {
        self.origin = origin
        self.destination = destination
        self.date = date
        self.useService = useService
        self.flightNumber = flightNumber
        self.departureTime = departureTime
    }

    private func parsedDate() -> Date? {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter.date(from: date)
    }

    private func writeSummary(_ summary: [String: Any]) {
        if JSONSerialization.isValidJSONObject(summary),
           let data = try? JSONSerialization.data(withJSONObject: summary, options: [.prettyPrinted, .sortedKeys]),
           let text = String(data: data, encoding: .utf8) {
            print(text)
        } else {
            print("Failed to encode summary")
        }
    }

    @MainActor
    private func runFetcherMode() async {
        logSmoke("starting fetcher \(origin)->\(destination) on \(date)")
        let fetcher = JSXWebViewFetcher()
        let result = await fetcher.fetchAvailability(
            origin: origin,
            destination: destination,
            date: date
        )
        logSmoke("fetcher returned")

        let flights = result.flights.map { flight in
            [
                "flightNumber": flight.flightNumber,
                "origin": flight.origin,
                "destination": flight.destination,
                "departureLocal": flight.departureLocal,
                "arrivalLocal": flight.arrivalLocal,
                "stops": flight.stops,
                "equipmentType": flight.equipmentType ?? "",
                "totalAvailable": flight.totalAvailable,
                "lowestFareTotal": flight.lowestFareTotal ?? NSNull(),
                "classes": flight.classes.map { fareClass in
                    [
                        "classOfService": fareClass.classOfService,
                        "productClass": fareClass.productClass,
                        "availableCount": fareClass.availableCount,
                        "fareTotal": fareClass.fareTotal,
                        "revenueTotal": fareClass.revenueTotal,
                        "fareBasisCode": fareClass.fareBasisCode ?? NSNull()
                    ]
                }
            ] as [String: Any]
        }

        var summary: [String: Any] = [
            "mode": "fetcher",
            "origin": origin,
            "destination": destination,
            "date": date,
            "flightCount": result.flights.count,
            "rawSearchBodyLength": result.rawSearchBody?.count ?? 0,
            "error": result.error ?? NSNull(),
            "flights": flights
        ]

        if let lowFare = result.lowFareFallback {
            summary["lowFareFallback"] = [
                "date": lowFare.date,
                "available": lowFare.available,
                "lowestPrice": lowFare.lowestPrice as Any? ?? NSNull()
            ]
        }

        writeSummary(summary)

        // Verification requires true per-flight data, not only the route-level fallback.
        exitCode = (result.error == nil && !result.flights.isEmpty) ? 0 : 1
    }

    @MainActor
    private func runServiceMode() async {
        logSmoke("starting service \(flightNumber) \(origin)->\(destination) on \(date)"
            + (departureTime.map { " @ \($0)" } ?? ""))
        guard let queryDate = parsedDate() else {
            writeSummary([
                "mode": "service",
                "error": "invalid date \(date)"
            ])
            exitCode = 1
            return
        }

        let service = AirlineLoadService()
        let result = await service.fetchLoad(
            airlineCode: "XE",
            flightNumber: flightNumber,
            date: queryDate,
            origin: origin,
            destination: destination,
            departureTime: departureTime
        )
        logSmoke("service returned")

        let cabins = result?.cabins.map { cabin in
            [
                "name": cabin.name,
                "capacity": cabin.capacity,
                "booked": cabin.booked,
                "available": cabin.available,
                "revenueStandby": cabin.revenueStandby,
                "nonRevStandby": cabin.nonRevStandby,
                "waitListCount": cabin.waitListCount,
                "jumpSeat": cabin.jumpSeat
            ]
        } ?? []

        let summary: [String: Any] = [
            "mode": "service",
            "queryFlightNumber": flightNumber,
            "queryDepartureTime": departureTime ?? NSNull(),
            "origin": origin,
            "destination": destination,
            "date": date,
            "result": result.map { load in
                [
                    "airlineCode": load.airlineCode,
                    "flightNumber": load.flightNumber,
                    "totalAvailable": load.totalAvailable,
                    "totalCapacity": load.totalCapacity,
                    "cabins": cabins
                ] as [String: Any]
            } ?? NSNull()
        ]

        writeSummary(summary)

        let isPerFlightCabin = cabins.contains { ($0["name"] as? String) == "Cabin" }
        exitCode = (result != nil && isPerFlightCabin) ? 0 : 1
    }

    @MainActor
    func start() async {
        if useService {
            await runServiceMode()
        } else {
            await runFetcherMode()
        }
        NSApp.terminate(nil)
    }
}

@main
struct JSXSwiftSmokeMain {
    static func main() {
        let env = ProcessInfo.processInfo.environment
        let origin = (env["JSX_ORIGIN"] ?? "DAL").uppercased()
        let destination = (env["JSX_DESTINATION"] ?? "HOU").uppercased()
        let date = env["JSX_DATE"] ?? "2026-04-15"
        let useService = env["JSX_USE_SERVICE"] == "1"
        let flightNumber = env["JSX_FLIGHT_NUMBER"] ?? "XE280"
        let departureTime = env["JSX_DEPARTURE_TIME"]

        let app = NSApplication.shared
        let delegate = JSXSwiftSmokeApp(
            origin: origin,
            destination: destination,
            date: date,
            useService: useService,
            flightNumber: flightNumber,
            departureTime: departureTime
        )
        logSmoke("booting app")
        app.setActivationPolicy(.prohibited)
        Timer.scheduledTimer(withTimeInterval: 120, repeats: false) { _ in
            logSmoke("timeout after 120s")
            NSApp.terminate(nil)
        }
        Task { @MainActor in
            await delegate.start()
        }
        app.run()
        exit(delegate.exitCode)
    }
}
