import SwiftUI
import NucleicProtocol

/// Answer UI for Claude Code's built-in `AskUserQuestion` tool — the mobile mirror of
/// the Mac's `AskUserQuestionBar`. Unlike a permission gate, this *collects* the user's
/// choice per question and folds the selections into the approval reply's `updatedInput`
/// (see `AskUserQuestion`); replying with a plain allow makes the CLI report "the user
/// did not answer the questions", which is exactly the bug the generic `ApprovalCardView`
/// produced here before.
///
/// Questions are shown one at a time as a wizard — Back/Next between them, Submit on the
/// last — so a multi-question ask reads as a focused sequence rather than one long form.
struct AskUserQuestionCardView: View {
    @EnvironmentObject var store: RemoteStore
    let approval: ApprovalRequest
    let questions: [AskUserQuestion.Question]
    /// The transcript's on-screen height, so a long question (many options / long descriptions) can
    /// scroll inside a bounded region instead of growing the card past the screen and pushing the
    /// Deny / Back / Next / Submit buttons out of reach. 0 until measured.
    var availableHeight: CGFloat = 0

    /// Selected option labels per question text.
    @State private var selected: [String: Set<String>] = [:]
    /// Whether the free-text "Other" answer is active per question text.
    @State private var otherActive: [String: Bool] = [:]
    /// The free-text "Other" answer per question text.
    @State private var otherText: [String: String] = [:]
    /// Index of the question currently shown (the wizard step).
    @State private var step = 0

    private var submitEnabled: Bool { store.connectivity.isLive }

    var body: some View {
        VStack(alignment: .leading, spacing: 14) {
            HStack(spacing: 6) {
                Image(systemName: "questionmark.bubble.fill").foregroundStyle(Palette.attention)
                Text(questions.count > 1 ? "Questions" : "Question")
                    .font(.subheadline.weight(.semibold))
            }

            if let question = currentQuestion {
                BoundedCardScroll(maxHeight: middleMaxHeight) {
                    VStack(alignment: .leading, spacing: 8) {
                        if questions.count > 1 {
                            Text("Question \(step + 1) of \(questions.count)")
                                .font(.caption).foregroundStyle(.secondary)
                        }
                        if let header = question.header, !header.isEmpty {
                            Text(header.uppercased())
                                .font(.caption).bold().foregroundStyle(.secondary)
                        }
                        Text(question.question).font(.headline)
                        ForEach(question.options) { option in
                            optionRow(question, label: option.label, description: option.description)
                        }
                        otherRow(question)
                    }
                }
            }

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

                if step > 0 {
                    Button("Back") { step -= 1 }
                        .buttonStyle(.bordered)
                }

                if isLastStep {
                    Button {
                        submit()
                    } label: {
                        Text("Submit").frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.borderedProminent)
                    .disabled(!allAnswered || !submitEnabled)
                    .keyboardShortcut(.defaultAction)
                } else {
                    Button {
                        step += 1
                    } label: {
                        Text("Next").frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.borderedProminent)
                    .disabled(!currentAnswered)
                    .keyboardShortcut(.defaultAction)
                }
            }
        }
        .padding()
        .glassSurface(cornerRadius: 20)
        // A hard ceiling on the whole card so it can never exceed the screen; `nil` (before the
        // height is measured) leaves it unconstrained.
        .frame(maxHeight: availableHeight > 0 ? availableHeight - 12 : nil, alignment: .bottom)
    }

    /// The height the scrolling question region may take before it starts scrolling — the screen
    /// minus a reserve for the header and the button row, so those always have room. Falls back to
    /// a roomy default before the height is measured.
    private var middleMaxHeight: CGFloat {
        let base = availableHeight > 0 ? availableHeight - 12 : 640
        return max(160, base - 170)
    }

    // MARK: - Wizard state

    /// The question for the active step, or `nil` if the index is somehow out of range.
    private var currentQuestion: AskUserQuestion.Question? {
        questions.indices.contains(step) ? questions[step] : nil
    }

    private var isLastStep: Bool { step >= questions.count - 1 }

    /// Whether the question on the current step has at least one answer — gates `Next`.
    private var currentAnswered: Bool {
        guard let currentQuestion else { return false }
        return !answers(for: currentQuestion).isEmpty
    }

    private var allAnswered: Bool {
        questions.allSatisfy { !answers(for: $0).isEmpty }
    }

    // MARK: - Rows

    @ViewBuilder
    private func optionRow(_ question: AskUserQuestion.Question, label: String, description: String?)
        -> some View
    {
        let isOn = selected[question.question]?.contains(label) ?? false
        Button { toggle(question, label) } label: {
            HStack(alignment: .top, spacing: 8) {
                Image(systemName: symbol(multiSelect: question.multiSelect, on: isOn))
                    .foregroundStyle(isOn ? Palette.attention : .secondary)
                VStack(alignment: .leading, spacing: 2) {
                    Text(label).font(.body)
                        .fixedSize(horizontal: false, vertical: true)
                    if let description, !description.isEmpty {
                        // Let the subtext wrap freely — inside a Button label with a trailing
                        // Spacer, an unconstrained Text truncates to one line and hides the rest.
                        Text(description).font(.subheadline).foregroundStyle(.secondary)
                            .fixedSize(horizontal: false, vertical: true)
                    }
                }
                Spacer(minLength: 0)
            }
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
    }

    @ViewBuilder
    private func otherRow(_ question: AskUserQuestion.Question) -> some View {
        let isOn = otherActive[question.question] ?? false
        HStack(alignment: .center, spacing: 8) {
            Button { toggleOther(question) } label: {
                Image(systemName: symbol(multiSelect: question.multiSelect, on: isOn))
                    .foregroundStyle(isOn ? Palette.attention : .secondary)
            }
            .buttonStyle(.plain)
            TextField(
                "Other…",
                text: Binding(
                    get: { otherText[question.question] ?? "" },
                    set: { newValue in
                        otherText[question.question] = newValue
                        if !newValue.isEmpty { activateOther(question) }
                    })
            )
            .textFieldStyle(.roundedBorder)
        }
    }

    private func symbol(multiSelect: Bool, on: Bool) -> String {
        if multiSelect { return on ? "checkmark.square.fill" : "square" }
        return on ? "largecircle.fill.circle" : "circle"
    }

    // MARK: - Selection

    private func toggle(_ question: AskUserQuestion.Question, _ label: String) {
        var set = selected[question.question] ?? []
        if question.multiSelect {
            if set.contains(label) { set.remove(label) } else { set.insert(label) }
        } else {
            set = set.contains(label) ? [] : [label]
            otherActive[question.question] = false
        }
        selected[question.question] = set
    }

    private func toggleOther(_ question: AskUserQuestion.Question) {
        let next = !(otherActive[question.question] ?? false)
        otherActive[question.question] = next
        if next, !question.multiSelect { selected[question.question] = [] }
    }

    private func activateOther(_ question: AskUserQuestion.Question) {
        otherActive[question.question] = true
        if !question.multiSelect { selected[question.question] = [] }
    }

    /// Chosen labels for a question: picked options plus any non-empty "Other".
    private func answers(for question: AskUserQuestion.Question) -> [String] {
        var labels = Array(selected[question.question] ?? [])
        if otherActive[question.question] ?? false {
            let custom = (otherText[question.question] ?? "")
                .trimmingCharacters(in: .whitespacesAndNewlines)
            if !custom.isEmpty { labels.append(custom) }
        }
        return labels
    }

    // MARK: - Submit

    private func submit() {
        var selections: [String: [String]] = [:]
        for question in questions { selections[question.question] = answers(for: question) }
        let updatedInput = AskUserQuestion.updatedInput(
            original: approval.input, selections: selections)
        respond(.allow(updatedInput: updatedInput))
    }

    private func respond(_ decision: Decision) {
        store.respond(approval, decision)
    }
}
