// Sources/UI/Pairing/PairingFlowView.swift
// T-2.2: SwiftUI pairing flow (idle → scanning → pairing → success/error)

import SwiftUI

// MARK: - State machine

private enum PairingState: Sendable {
    case idle
    case scanning
    case pairing(qrPayload: QRPayload)
    case success(credential: SidecarCredential)
    case error(message: String, qrPayload: QRPayload?)
}

/// Intermediate value capturing a parsed QR result.
private struct QRPayload: Sendable {
    let host: String
    let port: Int
    let pairingToken: String
    let fingerprint: String
    let name: String
}

// MARK: - View

struct PairingFlowView: View {

    /// Called with the new credential after pairing succeeds.
    var onSuccess: ((SidecarCredential) -> Void)?

    @State private var state: PairingState = .idle

    private let service = PairingService()

    var body: some View {
        Group {
            switch state {
            case .idle:
                idleView
            case .scanning:
                scanningView
            case .pairing:
                pairingView
            case .success(let credential):
                successView(credential: credential)
            case .error(let message, let payload):
                errorView(message: message, retryPayload: payload)
            }
        }
        .animation(.default, value: stateTag)
    }

    // MARK: - Sub-views

    private var idleView: some View {
        VStack(spacing: 24) {
            Image(systemName: "qrcode.viewfinder")
                .resizable()
                .scaledToFit()
                .frame(width: 100, height: 100)
                .foregroundStyle(.secondary)

            Text("Pair with Pi Remote")
                .font(.title2)
                .bold()

            Text("Scan the QR code shown by `pi-remote pair` on your server.")
                .multilineTextAlignment(.center)
                .foregroundStyle(.secondary)

            Button(action: { state = .scanning }) {
                Label("Tap to Scan", systemImage: "camera")
                    .frame(maxWidth: .infinity)
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
        }
        .padding(32)
    }

    private var scanningView: some View {
        ZStack(alignment: .top) {
            QRScannerView { rawString in
                handleQRScan(rawString)
            }
            .ignoresSafeArea()

            VStack {
                HStack {
                    Button(action: { state = .idle }) {
                        Label("Cancel", systemImage: "xmark.circle.fill")
                            .labelStyle(.iconOnly)
                            .font(.title)
                            .foregroundStyle(.white)
                    }
                    Spacer()
                }
                .padding()

                Text("Point at the pi-remote QR code")
                    .font(.subheadline)
                    .foregroundStyle(.white)
                    .padding(.horizontal, 12)
                    .padding(.vertical, 6)
                    .background(.ultraThinMaterial, in: Capsule())
                    .padding(.top, 4)
            }
        }
    }

    private var pairingView: some View {
        VStack(spacing: 20) {
            ProgressView()
                .controlSize(.large)
            Text("Pairing…")
                .foregroundStyle(.secondary)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    private func successView(credential: SidecarCredential) -> some View {
        VStack(spacing: 24) {
            Image(systemName: "checkmark.circle.fill")
                .resizable()
                .scaledToFit()
                .frame(width: 80, height: 80)
                .foregroundStyle(.green)

            Text("Connected to \(credential.sidecarName)")
                .font(.title2)
                .bold()
                .multilineTextAlignment(.center)

            Text("Paired on \(credential.pairedAt.formatted(date: .abbreviated, time: .shortened))")
                .foregroundStyle(.secondary)
                .font(.subheadline)

            Button("Done") {
                onSuccess?(credential)
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
        }
        .padding(32)
    }

    private func errorView(message: String, retryPayload: QRPayload?) -> some View {
        VStack(spacing: 24) {
            Image(systemName: "exclamationmark.triangle.fill")
                .resizable()
                .scaledToFit()
                .frame(width: 80, height: 80)
                .foregroundStyle(.orange)

            Text("Pairing Failed")
                .font(.title2)
                .bold()

            Text(message)
                .multilineTextAlignment(.center)
                .foregroundStyle(.secondary)

            Button(action: retryAction(for: retryPayload)) {
                Label("Retry", systemImage: "arrow.counterclockwise")
                    .frame(maxWidth: .infinity)
            }
            .buttonStyle(.bordered)
            .controlSize(.large)
        }
        .padding(32)
    }

    // MARK: - Logic

    private func retryAction(for payload: QRPayload?) -> () -> Void {
        if let payload {
            // Retry the exchange with the already-parsed QR data.
            return { startExchange(payload: payload) }
        } else {
            // QR parse failed — let the user scan again.
            return { state = .scanning }
        }
    }

    private func handleQRScan(_ rawString: String) {
        let payload: QRPayload
        do {
            let parsed = try PairingService.parseQR(rawString)
            payload = QRPayload(
                host: parsed.host,
                port: parsed.port,
                pairingToken: parsed.pairingToken,
                fingerprint: parsed.fingerprint,
                name: parsed.name
            )
        } catch {
            state = .error(message: "Invalid QR code — please try again.", qrPayload: nil)
            return
        }

        startExchange(payload: payload)
    }

    private func startExchange(payload: QRPayload) {
        state = .pairing(qrPayload: payload)

        let deviceName = UIDevice.current.name

        Task { @MainActor in
            do {
                let credential = try await service.exchange(
                    host: payload.host,
                    port: payload.port,
                    pairingToken: payload.pairingToken,
                    fingerprint: payload.fingerprint,
                    name: payload.name,
                    deviceName: deviceName
                )
                try Keychain.shared.save(credential, key: Keychain.credentialKey)
                state = .success(credential: credential)
            } catch let err as PairingError {
                state = .error(message: friendlyMessage(for: err), qrPayload: payload)
            } catch {
                state = .error(message: error.localizedDescription, qrPayload: payload)
            }
        }
    }

    private func friendlyMessage(for error: PairingError) -> String {
        switch error {
        case .invalidQR:
            return "The QR code doesn't look like a pi-remote URL."
        case .networkError(let underlying):
            return "Network error: \(underlying.localizedDescription)"
        case .serverError(let code, let body):
            return "Server returned \(code): \(body)"
        case .decodingFailed:
            return "Couldn't understand the server's response."
        }
    }

    // MARK: - Helpers

    /// Computed tag for `animation(value:)` — just needs to change when state changes.
    private var stateTag: Int {
        switch state {
        case .idle:    return 0
        case .scanning: return 1
        case .pairing:  return 2
        case .success:  return 3
        case .error:    return 4
        }
    }
}

// MARK: - Preview

#Preview {
    PairingFlowView()
}
