import CoreLocation
import SwiftUI

/// The idle surface of the ride flow: balance, scan-to-ride, nearby scooters.
///
/// Everything from allocation onward belongs to `RideFlowHost`  this view's only job
/// is to hand a QR code to `onScan`.
///
/// The map and the nearby list show the same scooters on purpose: the list is the
/// accessible equivalent of the map.
struct HomeView: View {
    /// Where the map opens before the first fix arrives. The view model reports the real origin
    /// it searched from, and the map follows that, so this is only ever the first frame.
    private static let fallbackCentre = CLLocationCoordinate2D(
        latitude: LocationProvider.fallbackLat,
        longitude: LocationProvider.fallbackLng
    )

    @StateObject private var vm: HomeViewModel
    @State private var qr = ""
    @State private var showScanner = false
    @State private var manualEntry = false
    @State private var selected: NearbyScooter?

    let onScan: (String) -> Void
    let onTopUp: () -> Void

    init(container: AppContainer, onScan: @escaping (String) -> Void, onTopUp: @escaping () -> Void) {
        _vm = StateObject(
            wrappedValue: HomeViewModel(
                rides: container.rideRepository,
                wallet: container.walletRepository,
                zones: container.zoneRepository,
                location: container.location
            )
        )
        self.onScan = onScan
        self.onTopUp = onTopUp
    }

    var body: some View {
        NavigationStack {
            ScrollView {
                VStack(spacing: 12) {
                    balanceCard
                    scanCard
                    if let error = vm.error {
                        Text(error)
                            .font(.beebCaption)
                            .foregroundStyle(Color.danger)
                            .frame(maxWidth: .infinity, alignment: .leading)
                    }
                    mapSection
                    nearbySection
                }
                .padding(16)
            }
            .background(Color.appBackground)
            .navigationTitle("home_title".localized)
        }
        // The map is a live view, not a snapshot. Without this it showed whatever was true when
        // the screen opened: a vehicle taken by another rider, moved into maintenance, or drained
        // since then stayed on the map. `.task` is cancelled when the view goes away, and SwiftUI
        // suspends it in the background, so this stops polling on its own.
        .task {
            var first = true
            while !Task.isCancelled {
                vm.refresh(silent: !first)
                first = false
                try? await Task.sleep(nanoseconds: 20_000_000_000)
            }
        }
        .fullScreenCover(isPresented: $showScanner) {
            QrScannerView(
                onResult: { code in
                    showScanner = false
                    onScan(code)
                },
                onManualEntry: {
                    showScanner = false
                    manualEntry = true
                },
                onClose: { showScanner = false }
            )
        }
    }

    private var balanceText: String {
        guard let balance = vm.balance else { return "" }
        return RideFormat.money(balance.balance, balance.currency)
    }

    private var balanceCard: some View {
        HStack {
            VStack(alignment: .leading, spacing: 2) {
                Text("wallet_balance".localized)
                    .font(.beebCaption)
                    .foregroundStyle(Color.muted)
                Text(balanceText)
                    .font(.beebTitle)
                    .foregroundStyle(Color.brand)
                    .numeric()
            }
            Spacer()
            SecondaryButton(title: "top_up".localized, action: onTopUp)
                .frame(width: 130)
        }
        .padding(18)
        .beebCard()
    }

    private var scanCard: some View {
        VStack(alignment: .leading, spacing: 14) {
            HStack(spacing: 12) {
                IconChip(glyph: "⛶", tone: .brand)
                Text("scan_to_ride".localized).font(.beebSection)
            }

            // Scanning is the primary path; typing a code is the fallback for a
            // scratched sticker or a denied camera, so it stays one tap away.
            BrandButton(title: "scan_with_camera".localized) { showScanner = true }

            if manualEntry {
                TextField("qr_hint".localized, text: $qr)
                    .textFieldStyle(.plain)
                    .padding(14)
                    .background(Color.surfaceRaised, in: RoundedRectangle(cornerRadius: BeebRadius.field))
                    .overlay(
                        RoundedRectangle(cornerRadius: BeebRadius.field)
                            .strokeBorder(Color.line, lineWidth: 1)
                    )
                SecondaryButton(
                    title: "start_ride".localized,
                    enabled: !qr.trimmingCharacters(in: .whitespaces).isEmpty
                ) {
                    onScan(qr)
                    qr = ""
                }
            } else {
                SecondaryButton(title: "scan_manual".localized) { manualEntry = true }
            }
        }
        .padding(18)
        .beebCard()
    }

    private var mapSection: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("map_title".localized)
                .font(.beebLabel)
                .foregroundStyle(Color.muted)
                .padding(.top, 8)

            ScooterMap(
                scooters: vm.nearby,
                parking: vm.parking,
                center: vm.origin.isReal
                    ? CLLocationCoordinate2D(latitude: vm.origin.lat, longitude: vm.origin.lng)
                    : Self.fallbackCentre,
                selectedId: selected?.id,
                onSelect: { selected = $0 }
            )
            .frame(height: 260)

            if let selected {
                // Tapping a pin already identifies the scooter, so connect with its own code
                // rather than sending the rider to scan a vehicle they have just picked. The
                // scanner and manual entry stay available for a scooter that is not on the map.
                SelectedScooterCard(scooter: selected) { onScan(selected.qrCode) }
            }
        }
    }

    private var nearbySection: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("nearby".localized)
                .font(.beebLabel)
                .foregroundStyle(Color.muted)
                .padding(.top, 8)

            ForEach(vm.nearby) { scooter in
                HStack {
                    IconChip(glyph: "⚡︎", tone: .brand)
                    VStack(alignment: .leading, spacing: 4) {
                        Text(scooter.qrCode).fontWeight(.semibold)
                        StatusBadge(
                            text: "\(scooter.batteryPercent)%",
                            tone: scooter.batteryPercent < 20 ? .warning : .success
                        )
                    }
                    Spacer()
                    Text(String(format: "%.0f m", scooter.distanceMeters))
                        .font(.beebBody)
                        .foregroundStyle(Color.muted)
                        .numeric()
                }
                .padding(16)
                .beebCard()
            }
        }
    }
}
