import SwiftUI
import LocalAuthentication
import NucleicProtocol

/// The defining interaction (UX_IOS §5): answer an approval. High-risk (destructive/network)
/// requires Face ID/Touch ID before Allow is enabled — deliberate friction mirroring the Mac.
/// First-responder-wins: if the Mac (or another phone) answers first, the host's
/// `approvalResolved` removes this card.
struct ApprovalCardView: View {
    @EnvironmentObject var store: RemoteStore
    let approval: ApprovalRequest

    @State private var biometricPassed = false
    @State private var authError: String?
    @State private var showAlwaysMenu = false

    private var requiresBiometric: Bool { approval.risk.isHigh }
    private var allowEnabled: Bool {
        store.connectivity.isLive && (!requiresBiometric || biometricPassed)
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack(spacing: 6) {
                Image(systemName: "exclamationmark.triangle.fill").foregroundStyle(.orange)
                Text("Permission requested").font(.subheadline.weight(.semibold))
            }
            HStack(spacing: 6) {
                Text(approval.toolName).font(.caption.weight(.bold))
                Text(approval.risk.label)
                    .font(.caption2.weight(.semibold))
                    .padding(.horizontal, 6).padding(.vertical, 2)
                    .background(approval.risk.color.opacity(0.2), in: Capsule())
                    .foregroundStyle(approval.risk.color)
            }
            // A host_exec gate escapes the sandbox onto the macOS host, so lay out what it's
            // about to run — the inferred purpose and the program/actions/flags, parsed from the
            // command itself (never from the agent) — then the exact command beneath it. Every
            // other tool shows its untruncated detail in a single scrollable box.
            if approval.toolName == HostCommandSummary.hostExecToolName,
               let command = approval.input["command"]?.stringValue,
               let parsed = HostCommandSummary.summary(for: command) {
                HostCommandBreakdown(summary: parsed)
                Text("Exact command").font(.caption2.weight(.semibold)).foregroundStyle(.secondary)
                ApprovalDetailBox(text: command)
            } else if !approval.input.approvalDetail.isEmpty {
                ApprovalDetailBox(text: approval.input.approvalDetail)
            }

            if requiresBiometric && !biometricPassed {
                Button {
                    authenticate()
                } label: {
                    Label("Face ID required to allow", systemImage: "faceid").font(.caption)
                }
                .buttonStyle(.bordered)
            }
            if let authError {
                Text(authError).font(.caption2).foregroundStyle(.red)
            }

            HStack(spacing: 10) {
                Button(role: .destructive) {
                    store.respond(approval, .deny(reason: nil))
                } label: {
                    Text("Deny").frame(maxWidth: .infinity)
                }
                .buttonStyle(.bordered)

                Button {
                    store.respond(approval, .allow())
                } label: {
                    Text("Allow").frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .disabled(!allowEnabled)
            }

            if !store.capabilities.allowAlwaysScopes.isEmpty {
                Menu("Allow always…") {
                    ForEach(store.capabilities.allowAlwaysScopes, id: \.self) { scope in
                        Button(alwaysLabel(scope)) {
                            store.respond(approval, .allowAlways(scope))
                        }
                        .disabled(!allowEnabled)
                    }
                }
                .font(.caption)
            }
        }
        .padding()
        .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 14))
        .onChange(of: approval.id) { _, _ in biometricPassed = false; authError = nil }
    }

    private func alwaysLabel(_ scope: AlwaysScope) -> String {
        switch scope {
        case .session: "This command, this session"
        case .toolName: "Any \(approval.toolName), this session"
        case .toolNameWithPattern: "\(approval.toolName) matching this pattern"
        }
    }

    private func authenticate() {
        let context = LAContext()
        var error: NSError?
        guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            authError = "Biometrics unavailable"; return
        }
        context.evaluatePolicy(
            .deviceOwnerAuthenticationWithBiometrics,
            localizedReason: "Approve a \(approval.risk.label) action"
        ) { success, evalError in
            Task { @MainActor in
                if success { biometricPassed = true; authError = nil }
                else { authError = evalError?.localizedDescription ?? "Authentication failed" }
            }
        }
    }
}

/// The full content of an approval, wrapped and selectable. The box sizes to its
/// content and only begins scrolling once the content exceeds `maxHeight`, so a short
/// command sits in a snug box while a large paste stays bounded. The text wraps; it
/// never scrolls horizontally.
private struct ApprovalDetailBox: View {
    let text: String
    private let maxHeight: CGFloat = 200
    @State private var contentHeight: CGFloat = 0

    var body: some View {
        ScrollView(.vertical) {
            Text(text)
                .font(.caption.monospaced())
                .textSelection(.enabled)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(8)
                .background(GeometryReader { geo in
                    Color.clear.preference(key: ApprovalDetailHeightKey.self, value: geo.size.height)
                })
        }
        .frame(height: contentHeight == 0 ? nil : min(contentHeight, maxHeight))
        .onPreferenceChange(ApprovalDetailHeightKey.self) { contentHeight = $0 }
        .background(Color(.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 8))
    }
}

private struct ApprovalDetailHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = max(value, nextValue())
    }
}
