import SwiftUI
import SwiftData

/// Top-level tab container.
///
/// Tab 1: the existing search / connection / where-to-go home screen.
/// Tab 2: the live flight tracker (map + filters + tap-to-detail).
/// Tab 3: personal flight history (logbook + stats + map).
///
/// Also subscribes to WalletPassObserver so that adding a boarding
/// pass to Apple Wallet pops the add-flight sheet over whatever tab
/// the user is on.
struct RootView: View {
    let database: AirportDatabase
    let loadService: AirlineLoadService
    let routeExplorer: RouteExplorerClient
    let openSky: OpenSkyClient
    let fr24: FR24Client
    let flightAware: FlightAwareScheduleClient

    @State private var selectedTab: Tab = .search
    @StateObject private var wallet = WalletPassObserver.shared
    @StateObject private var integrityMonitor = DataIntegrityMonitor.shared
    @State private var bannerDismissed = false
    @State private var saveBannerDismissedCount: Int = 0
    @State private var walletPrefill: AddFlightView.Prefill?
    /// URL-scheme prefill (from the Share Extension or any external
    /// invocation of `flights://import?...`).
    @State private var urlPrefill: AddFlightView.Prefill?
    @Environment(\.modelContext) private var modelContext

    enum Tab: Hashable { case search, live, history, settings }

    private var showIntegrityBanner: Bool {
        integrityMonitor.hasFailures && !bannerDismissed
    }

    /// Save-failure banner stays up until either the user dismisses the
    /// *current* count or new failures arrive after dismissal. We compare
    /// the current save-failure count to the snapshot at dismiss-time so
    /// a brand-new failure re-shows the banner.
    private var showSaveBanner: Bool {
        integrityMonitor.saveFailures.count > saveBannerDismissedCount
    }

    var body: some View {
        tabs
            .overlay(alignment: .top) {
                VStack(spacing: 0) {
                    if showSaveBanner {
                        saveFailureBanner
                            .transition(.move(edge: .top).combined(with: .opacity))
                    }
                    if showIntegrityBanner {
                        integrityBanner
                            .transition(.move(edge: .top).combined(with: .opacity))
                    }
                }
            }
            .animation(.easeInOut(duration: 0.2), value: showIntegrityBanner)
            .animation(.easeInOut(duration: 0.2), value: showSaveBanner)
    }

    private var integrityBanner: some View {
        HStack(spacing: 8) {
            Text("⚠️ Some reference data didn't load")
                .font(.footnote.weight(.semibold))
                .foregroundStyle(.black)
                .lineLimit(2)
                .accessibilityLabel("Some reference data did not load")
            Spacer(minLength: 4)
            Button {
                bannerDismissed = true
            } label: {
                Image(systemName: "xmark")
                    .font(.footnote.weight(.bold))
                    .foregroundStyle(.black)
                    .padding(6)
                    .contentShape(Rectangle())
            }
            .accessibilityLabel("Dismiss banner")
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
        .frame(maxWidth: .infinity)
        .background(FlightTheme.delayed)
    }

    /// Red banner shown when a SwiftData save throws. Distinct from the
    /// yellow decode-failure banner because the action is different — the
    /// user needs to know their *edit* didn't persist (and so anything
    /// they typed may be lost if they background the app).
    private var saveFailureBanner: some View {
        let latest = integrityMonitor.saveFailures.last ?? ""
        return HStack(spacing: 8) {
            VStack(alignment: .leading, spacing: 2) {
                Text("❌ Your last edit didn't save")
                    .font(.footnote.weight(.bold))
                    .foregroundStyle(.white)
                if !latest.isEmpty {
                    Text(latest)
                        .font(.caption2)
                        .foregroundStyle(.white.opacity(0.9))
                        .lineLimit(1)
                        .truncationMode(.middle)
                }
            }
            .accessibilityLabel("Your last edit did not save. \(latest)")
            Spacer(minLength: 4)
            Button {
                saveBannerDismissedCount = integrityMonitor.saveFailures.count
            } label: {
                Image(systemName: "xmark")
                    .font(.footnote.weight(.bold))
                    .foregroundStyle(.white)
                    .padding(6)
                    .contentShape(Rectangle())
            }
            .accessibilityLabel("Dismiss save-failure banner")
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
        .frame(maxWidth: .infinity)
        .background(FlightTheme.cancelled)
    }

    private var tabs: some View {
        TabView(selection: $selectedTab) {
            RoutePlannerView(
                database: database,
                client: routeExplorer,
                flightAware: flightAware,
                loadService: loadService
            )
            .tabItem {
                Label("Search", systemImage: "magnifyingglass")
            }
            .tag(Tab.search)

            NavigationStack {
                LiveFlightsView(
                    openSky: openSky,
                    fr24: fr24,
                    routeExplorer: routeExplorer,
                    database: database
                )
                .navigationTitle("Live Flights")
                .navigationBarTitleDisplayMode(.inline)
            }
            .tabItem {
                Label("Live", systemImage: "antenna.radiowaves.left.and.right")
            }
            .tag(Tab.live)

            NavigationStack {
                HistoryView(
                    database: database,
                    routeExplorer: routeExplorer,
                    openSky: openSky
                )
            }
            .tabItem {
                Label("History", systemImage: "book.closed")
            }
            .tag(Tab.history)

            SettingsView()
                .tabItem {
                    Label("Settings", systemImage: "gear")
                }
                .tag(Tab.settings)
        }
        .tint(FlightTheme.accent)
        .task {
            // Defer PKPassLibrary initialization until the first task
            // run, so app launch isn't blocked by it.
            wallet.start()
        }
        .onChange(of: wallet.pendingPass) { _, pass in
            // A new boarding pass landed in Wallet — surface the
            // add-flight sheet pre-populated from it.
            guard let pass else { return }
            walletPrefill = AddFlightView.Prefill(
                flightDate: pass.flightDate,
                carrierICAO: nil,
                carrierIATA: pass.carrierIATA,
                flightNumber: pass.flightNumber,
                departureIATA: pass.departureIATA,
                arrivalIATA: pass.arrivalIATA,
                scheduledDeparture: pass.flightDate,
                scheduledArrival: nil,
                aircraftType: nil,
                registration: nil,
                icao24: nil,
                source: "wallet"
            )
        }
        .sheet(item: $walletPrefill) { prefill in
            let store = FlightHistoryStore(context: modelContext, airportDatabase: database)
            AddFlightView(
                routeExplorer: routeExplorer,
                database: database,
                store: store,
                prefill: prefill
            )
            .onDisappear { wallet.clearPending() }
        }
        .sheet(item: $urlPrefill) { prefill in
            let store = FlightHistoryStore(context: modelContext, airportDatabase: database)
            AddFlightView(
                routeExplorer: routeExplorer,
                database: database,
                store: store,
                prefill: prefill
            )
        }
        .onOpenURL { url in
            // Safari bookmarklet → flights://routeexplorer-token?token=…&exp=…&cookie=…
            // The token store handles the parse + persistence; we just
            // pop a confirmation if it took effect.
            if url.scheme == "flights", url.host == "routeexplorer-token" {
                let accepted = RouteExplorerTokenStore.shared.ingest(url: url)
                if accepted {
                    selectedTab = .settings
                }
                return
            }
            // Share Extension hands us a URL like:
            //   flights://import?carrier=WN&num=7&dep=DAL&arr=HOU&date=1779892800
            guard url.scheme == "flights", url.host == "import" else { return }
            let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
            let q = components?.queryItems ?? []
            func val(_ k: String) -> String? { q.first { $0.name == k }?.value }
            let dateInterval = val("date").flatMap(TimeInterval.init)
            let prefill = AddFlightView.Prefill(
                flightDate: dateInterval.map { Date(timeIntervalSince1970: $0) } ?? Date(),
                carrierICAO: nil,
                carrierIATA: val("carrier"),
                flightNumber: val("num"),
                departureIATA: val("dep"),
                arrivalIATA: val("arr"),
                scheduledDeparture: nil,
                scheduledArrival: nil,
                aircraftType: nil,
                registration: nil,
                icao24: nil,
                source: "mail-share"
            )
            selectedTab = .history
            urlPrefill = prefill
        }
    }
}

extension AddFlightView.Prefill: Identifiable {
    public var id: String {
        // Stable enough — pass-prompted prefills are one-at-a-time.
        "\(flightDate.timeIntervalSince1970)-\(carrierIATA ?? "")\(flightNumber ?? "")"
    }
}
