import Foundation
import SwiftData

/// Aggregate result of a personal standby-success query. All counts are
/// derived from the user's own LoggedFlight history; nothing is fetched
/// from the network. Sendable so it can cross actor boundaries safely.
struct StandbyRate: Sendable {
    /// standby-made + standby-bumped (i.e. every flight the user actually
    /// stood by for, regardless of outcome).
    let attempts: Int
    /// Outcome == "standby-made".
    let made: Int
    /// Outcome == "standby-bumped".
    let bumped: Int
    /// Outcome == "confirmed" — not a standby attempt, but useful for
    /// "out of N flights on this route, X were confirmed seats".
    let confirmed: Int
    /// made / attempts. Zero when there are no attempts.
    let rate: Double

    static let empty = StandbyRate(attempts: 0, made: 0, bumped: 0, confirmed: 0, rate: 0)
}

/// Computes personal standby success metrics from the local SwiftData
/// store. Filters are optional and combined with AND. The intent is to
/// answer questions like "what's my clear rate on WN out of DAL?".
@MainActor
final class StandbyStatsService {
    init() {}

    /// Personal standby clear rate, optionally narrowed by carrier and/or
    /// route endpoints. Carrier matches against both IATA and ICAO codes
    /// so the caller doesn't need to know which one was stored.
    func personalRate(
        carrier: String?,
        origin: String?,
        dest: String?,
        context: ModelContext
    ) -> StandbyRate {
        let flights = fetchFlightsWithStandbyOutcome(
            carrier: carrier,
            origin: origin,
            dest: dest,
            context: context
        )

        var made = 0
        var bumped = 0
        var confirmed = 0
        for f in flights {
            switch f.standbyOutcome {
            case "standby-made":   made += 1
            case "standby-bumped": bumped += 1
            case "confirmed":      confirmed += 1
            default:               break
            }
        }
        let attempts = made + bumped
        let rate = attempts > 0 ? Double(made) / Double(attempts) : 0

        print("[StandbyStats] personalRate carrier=\(carrier ?? "*") "
              + "origin=\(origin ?? "*") dest=\(dest ?? "*") "
              + "attempts=\(attempts) made=\(made) bumped=\(bumped) "
              + "confirmed=\(confirmed) rate=\(String(format: "%.2f", rate))")

        return StandbyRate(
            attempts: attempts,
            made: made,
            bumped: bumped,
            confirmed: confirmed,
            rate: rate
        )
    }

    /// Most recent flights that have any standby outcome set, newest
    /// first. Used by the History tab to show a "your last N standby
    /// attempts" strip.
    func recentOutcomes(limit: Int, context: ModelContext) -> [LoggedFlight] {
        var descriptor = FetchDescriptor<LoggedFlight>(
            predicate: #Predicate { $0.standbyOutcome != nil },
            sortBy: [SortDescriptor(\.flightDate, order: .reverse)]
        )
        descriptor.fetchLimit = max(0, limit)
        let results = (try? context.fetch(descriptor)) ?? []
        print("[StandbyStats] recentOutcomes limit=\(limit) returned=\(results.count)")
        return results
    }

    // MARK: - Private

    /// SwiftData's #Predicate macro is finicky about optional captures, so
    /// we apply the optional carrier/origin/dest filters in Swift after a
    /// single fetch of every flight with a non-nil standbyOutcome. The
    /// data set is bounded by the user's own flight history, so this is
    /// trivial in practice.
    private func fetchFlightsWithStandbyOutcome(
        carrier: String?,
        origin: String?,
        dest: String?,
        context: ModelContext
    ) -> [LoggedFlight] {
        let descriptor = FetchDescriptor<LoggedFlight>(
            predicate: #Predicate { $0.standbyOutcome != nil }
        )
        let all = (try? context.fetch(descriptor)) ?? []

        let carrierUpper = carrier?.uppercased()
        let originUpper = origin?.uppercased()
        let destUpper = dest?.uppercased()

        return all.filter { f in
            if let carrierUpper {
                let iata = f.carrierIATA?.uppercased()
                let icao = f.carrierICAO?.uppercased()
                if iata != carrierUpper && icao != carrierUpper { return false }
            }
            if let originUpper, f.departureIATA.uppercased() != originUpper { return false }
            if let destUpper, f.arrivalIATA.uppercased() != destUpper { return false }
            return true
        }
    }
}
