import SwiftUI
import NucleicProtocol

/// One tool call as a collapsible card — the mobile echo of the Mac's `ToolCallRow`. Collapsed
/// it shows the tool, a one-line input gist, and a running spinner; expanded it reveals the full
/// input, the result, and any files the call touched. When `inGroup` is set the card renders
/// flush (no background/border) because an enclosing `ToolBlockCard` supplies the single
/// contiguous container — exactly as the Mac groups a run of calls.
struct ToolCallCard: View {
    let group: ToolGroup
    /// Renders flush inside a `ToolBlockCard` (the block draws the one card) when true.
    var inGroup: Bool = false
    @State private var expanded = false

    var body: some View {
        let content = VStack(alignment: .leading, spacing: 8) {
            Button { expanded.toggle() } label: { header }.buttonStyle(.plain)
            if expanded { details }
        }
        if inGroup {
            content.padding(.horizontal, 10).padding(.vertical, 8)
        } else {
            content
                .padding(10)
                .background(Color(.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 10))
                .overlay(RoundedRectangle(cornerRadius: 10).strokeBorder(
                    group.isError ? Palette.danger.opacity(0.5) : Color.primary.opacity(0.05), lineWidth: 1))
        }
    }

    private var header: some View {
        HStack(spacing: 8) {
            Image(systemName: ToolGlyph.icon(group.name))
                .font(.caption).foregroundStyle(group.isError ? Palette.danger : .secondary)
            Text(group.displayName).font(.caption.weight(.semibold))
            Text(group.input.compactSummary)
                .font(.caption.monospaced()).foregroundStyle(.secondary).lineLimit(1)
            Spacer(minLength: 4)
            if !group.finished { ProgressView().controlSize(.mini) }
            Image(systemName: expanded ? "chevron.down" : "chevron.right")
                .font(.caption2).foregroundStyle(.tertiary)
        }
        .contentShape(Rectangle())
    }

    @ViewBuilder private var details: some View {
        let input = group.input.approvalDetail
        if !input.isEmpty {
            ToolBlock(label: "Input", text: input, mono: true)
        }
        if let result = group.result {
            ToolBlock(label: group.isError ? "Error" : "Result",
                      text: result.compactSummary, mono: true, danger: group.isError)
        }
        if !group.fileChanges.isEmpty {
            VStack(alignment: .leading, spacing: 3) {
                ForEach(group.fileChanges, id: \.path) { change in
                    Label(change.path, systemImage: ToolGlyph.fileChange(change.change))
                        .font(.caption2.monospaced()).foregroundStyle(.secondary).lineLimit(1)
                }
            }
        }
    }
}

/// A run of two or more consecutive tool calls rendered as ONE contiguous card — the mobile echo
/// of the Mac's `ToolGroupRow`. Collapsed it lists up to five one-line summaries (glyph + tool +
/// input gist) with a call count and a "See more"; tapping the block reveals every call in full,
/// each its own flush `ToolCallCard` with result and file changes.
struct ToolBlockCard: View {
    let groups: [ToolGroup]
    @State private var expanded = false

    /// A collapsed block shows at most this many summary lines before a "See more".
    private static let collapsedLineLimit = 5

    private var allFinished: Bool { groups.allSatisfy(\.finished) }
    private var hiddenCount: Int { max(0, groups.count - Self.collapsedLineLimit) }
    private var shownGroups: ArraySlice<ToolGroup> {
        expanded ? groups[...] : groups.prefix(Self.collapsedLineLimit)
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            header
                .contentShape(Rectangle())
                .onTapGesture { withAnimation(.easeInOut(duration: 0.15)) { expanded.toggle() } }
            if expanded {
                ForEach(groups, id: \.toolCallID) { group in
                    Divider().overlay(Color.primary.opacity(0.06))
                    ToolCallCard(group: group, inGroup: true)
                }
                Divider().overlay(Color.primary.opacity(0.06))
                collapseFooter
            }
        }
        .background(Color(.secondarySystemBackground), in: RoundedRectangle(cornerRadius: 10))
        .overlay(RoundedRectangle(cornerRadius: 10).strokeBorder(Color.primary.opacity(0.05), lineWidth: 1))
    }

    private var header: some View {
        VStack(alignment: .leading, spacing: 7) {
            ForEach(Array(shownGroups.enumerated()), id: \.element.toolCallID) { index, group in
                HStack(spacing: 8) {
                    Image(systemName: ToolGlyph.icon(group.name))
                        .font(.caption).foregroundStyle(group.isError ? Palette.danger : .secondary)
                        .frame(width: 16)
                    Text(group.displayName).font(.caption.weight(.semibold))
                    Text(group.input.compactSummary)
                        .font(.caption.monospaced()).foregroundStyle(.secondary).lineLimit(1)
                    Spacer(minLength: 4)
                    if index == 0 { trailingControls }
                }
            }
            if !expanded, hiddenCount > 0 {
                Text("See \(hiddenCount) more…")
                    .font(.caption.weight(.medium)).foregroundStyle(Palette.accent)
                    .padding(.leading, 24)
            }
        }
        .padding(10)
    }

    private var trailingControls: some View {
        HStack(spacing: 6) {
            Text("\(groups.count)")
                .font(.caption2.weight(.semibold).monospacedDigit()).foregroundStyle(.secondary)
            if !allFinished { ProgressView().controlSize(.mini) }
            Image(systemName: expanded ? "chevron.down" : "chevron.right")
                .font(.caption2).foregroundStyle(.tertiary)
        }
    }

    private var collapseFooter: some View {
        Button {
            withAnimation(.easeInOut(duration: 0.15)) { expanded = false }
        } label: {
            HStack(spacing: 6) {
                Image(systemName: "chevron.up").font(.caption2.weight(.semibold))
                Text("Collapse").font(.caption.weight(.medium))
                Spacer(minLength: 0)
            }
            .foregroundStyle(Palette.accent).contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .padding(.horizontal, 10).padding(.vertical, 6)
    }
}

// MARK: - Subagent & multi-agent cards
//
// Claude Code spawns subagents through the `Task` tool. Nucleic surfaces that delegated work in
// the orchestra gold so it reads distinctly from the agent's own tool calls: a lone `Task`
// becomes a `SubagentCard`, and a parallel wave of them becomes an `OrchestrationCard`. Both
// build from the projection's nested `children` (a subagent's inner Read/Bash/Edit/thinking),
// mirroring the Mac at phone fidelity.

/// The lifecycle state of a spawned subagent, read from its `Task` group: not finished → still
/// working; an error result → failed; otherwise → done.
enum SubagentRunState {
    case running, done, failed

    init(group: ToolGroup) {
        if !group.finished { self = .running }
        else if group.isError { self = .failed }
        else { self = .done }
    }

    var label: String {
        switch self {
        case .running: "Working"
        case .done: "Done"
        case .failed: "Failed"
        }
    }

    var color: Color {
        switch self {
        case .running: Palette.orchestra
        case .done: Palette.success
        case .failed: Palette.danger
        }
    }
}

/// The human-facing bits of a `Task` tool call, so the subagent cards agree on what to show.
enum Subagent {
    /// The subagent flavor (`subagent_type`: "Explore", "general-purpose", …), or nil.
    static func type(_ group: ToolGroup) -> String? {
        let raw = group.input["subagent_type"]?.stringValue?
            .trimmingCharacters(in: .whitespacesAndNewlines)
        return (raw?.isEmpty == false) ? raw : nil
    }

    /// The one-line task the parent handed the subagent (`description`), falling back to the
    /// full `prompt`, then a generic label.
    static func taskLabel(_ group: ToolGroup) -> String {
        if let description = group.input["description"]?.stringValue?
            .trimmingCharacters(in: .whitespacesAndNewlines), !description.isEmpty {
            return description
        }
        if let prompt = group.input["prompt"]?.stringValue?
            .trimmingCharacters(in: .whitespacesAndNewlines), !prompt.isEmpty {
            return prompt
        }
        return "Subagent task"
    }

    /// "Explore — find the composer", joining the flavor and task for a compact one-liner.
    static func headline(_ group: ToolGroup) -> String {
        let label = taskLabel(group)
        if let type = type(group) { return "\(type) — \(label)" }
        return label
    }

    /// The subagent's returned report, or nil while it's still working / reported nothing.
    static func report(_ group: ToolGroup) -> String? {
        guard let result = group.result else { return nil }
        let text = result.compactSummary
        return text.isEmpty ? nil : text
    }
}

/// A small leading status glyph for a subagent — a gold spinner while it works, a green check or
/// red cross once it settles.
struct SubagentStatusGlyph: View {
    let state: SubagentRunState
    var body: some View {
        Group {
            switch state {
            case .running: ProgressView().controlSize(.mini).tint(Palette.orchestra)
            case .done: Image(systemName: "checkmark.circle.fill").foregroundStyle(Palette.success)
            case .failed: Image(systemName: "xmark.octagon.fill").foregroundStyle(Palette.danger)
            }
        }
        .font(.caption).frame(width: 16)
    }
}

/// A status pill (spinner / check / cross + word) for a subagent card header.
struct SubagentStatusChip: View {
    let state: SubagentRunState
    var body: some View {
        HStack(spacing: 4) {
            SubagentStatusGlyph(state: state).frame(width: 14)
            Text(state.label)
        }
        .font(.caption2.weight(.medium))
        .foregroundStyle(state.color)
        .padding(.horizontal, 7).padding(.vertical, 3)
        .background(state.color.opacity(0.14), in: Capsule())
    }
}

/// One delegated subagent (`Task`), as a gold card: its type and the task it was given, a live
/// Working / Done / Failed status, and — on expand — its nested activity stream (or, for a
/// replayed transcript with none, its returned report). Mirrors the Mac's `SubagentCard`.
struct SubagentCard: View {
    let group: ToolGroup
    @State private var expanded = false

    private var state: SubagentRunState { SubagentRunState(group: group) }
    private var report: String? { Subagent.report(group) }
    private var hasDetail: Bool { !group.children.isEmpty || report != nil }

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            header
                .contentShape(Rectangle())
                .onTapGesture {
                    guard hasDetail else { return }
                    withAnimation(.easeInOut(duration: 0.15)) { expanded.toggle() }
                }
            if expanded, hasDetail {
                Divider().overlay(Palette.orchestra.opacity(0.2))
                SubagentActivityStream(children: group.children, fallbackReport: report)
            }
        }
        .background(Palette.orchestra.opacity(0.08))
        .overlay(RoundedRectangle(cornerRadius: 10).strokeBorder(Palette.orchestra.opacity(0.3), lineWidth: 1))
        .clipShape(RoundedRectangle(cornerRadius: 10))
    }

    private var header: some View {
        VStack(alignment: .leading, spacing: 6) {
            HStack(alignment: .firstTextBaseline, spacing: 10) {
                Image(systemName: "person.2.fill")
                    .font(.callout).foregroundStyle(Palette.orchestra).frame(width: 18)
                (Text("Subagent")
                    + (Subagent.type(group).map { Text(" · \($0)").foregroundColor(.secondary) } ?? Text("")))
                    .font(.callout.weight(.semibold))
                Spacer(minLength: 6)
                SubagentStatusChip(state: state)
                if hasDetail {
                    Image(systemName: expanded ? "chevron.up" : "chevron.down")
                        .font(.caption2).foregroundStyle(.secondary)
                }
            }
            Text(Subagent.taskLabel(group))
                .font(.caption).foregroundStyle(.secondary)
                .lineLimit(expanded ? nil : 2).truncationMode(.tail)
                .fixedSize(horizontal: false, vertical: true)
        }
        .padding(.horizontal, 12).padding(.vertical, 10)
    }
}

/// A multi-agent fan-out: a wave of two or more subagents (`Task` calls) spawned together. The
/// header sums up the wave (count + overall status); each subagent is its own row that expands
/// independently to reveal its activity stream and report. Mirrors the Mac's `OrchestrationCard`.
struct OrchestrationCard: View {
    let groups: [ToolGroup]

    /// Overall wave status: failed if any failed, else running if any is still working, else done.
    private var overall: SubagentRunState {
        let states = groups.map { SubagentRunState(group: $0) }
        if states.contains(where: { if case .failed = $0 { true } else { false } }) { return .failed }
        if states.contains(where: { if case .running = $0 { true } else { false } }) { return .running }
        return .done
    }
    private var doneCount: Int { groups.filter(\.finished).count }

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            header
            ForEach(groups, id: \.toolCallID) { group in
                Divider().overlay(Palette.orchestra.opacity(0.18))
                OrchestrationAgentRow(group: group)
            }
        }
        .background(Palette.orchestra.opacity(0.08))
        .overlay(RoundedRectangle(cornerRadius: 10).strokeBorder(Palette.orchestra.opacity(0.3), lineWidth: 1))
        .clipShape(RoundedRectangle(cornerRadius: 10))
    }

    private var header: some View {
        HStack(alignment: .firstTextBaseline, spacing: 10) {
            Image(systemName: "rectangle.3.group.fill")
                .font(.callout).foregroundStyle(Palette.orchestra).frame(width: 18)
            VStack(alignment: .leading, spacing: 1) {
                Text("Multiple Subagents").font(.callout.weight(.semibold))
                Text("\(groups.count) subagents · \(doneCount) done")
                    .font(.caption2).foregroundStyle(.secondary)
            }
            Spacer(minLength: 6)
            SubagentStatusChip(state: overall)
        }
        .padding(.horizontal, 12).padding(.vertical, 10)
    }
}

/// One subagent within a fan-out: a one-line status header that expands on tap to reveal that
/// agent's own activity stream — and, failing that, its returned report. Each row tracks its own
/// expansion so agents open independently.
private struct OrchestrationAgentRow: View {
    let group: ToolGroup
    @State private var expanded = false

    private var state: SubagentRunState { SubagentRunState(group: group) }
    private var report: String? { Subagent.report(group) }
    private var hasDetail: Bool { !group.children.isEmpty || report != nil }

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            header
                .contentShape(Rectangle())
                .onTapGesture {
                    guard hasDetail else { return }
                    withAnimation(.easeInOut(duration: 0.15)) { expanded.toggle() }
                }
            if expanded, hasDetail {
                SubagentActivityStream(children: group.children, fallbackReport: report)
            }
        }
    }

    private var header: some View {
        HStack(alignment: .firstTextBaseline, spacing: 9) {
            SubagentStatusGlyph(state: state)
            Text(Subagent.headline(group))
                .font(.caption).lineLimit(1).truncationMode(.tail)
            Spacer(minLength: 6)
            if hasDetail {
                Image(systemName: expanded ? "chevron.up" : "chevron.down")
                    .font(.caption2).foregroundStyle(.secondary)
            }
        }
        .padding(.horizontal, 12).padding(.vertical, 8)
    }
}

/// A subagent's nested activity — the calls, thinking, and prose it produced — rendered with the
/// same row views the main transcript uses, recursing into nested subagents' own cards. Falls
/// back to the returned report when there's no captured inner activity (a replayed transcript).
private struct SubagentActivityStream: View {
    let children: [TranscriptItem]
    var fallbackReport: String? = nil

    var body: some View {
        if !children.isEmpty {
            VStack(alignment: .leading, spacing: 8) {
                ForEach(children) { child in
                    TranscriptRow(item: child)
                }
            }
            .padding(.horizontal, 12).padding(.vertical, 8)
            .frame(maxWidth: .infinity, alignment: .leading)
        } else if let fallbackReport {
            Text(fallbackReport)
                .font(.caption).foregroundStyle(.secondary)
                .textSelection(.enabled)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(.horizontal, 12).padding(.vertical, 10)
        }
    }
}

/// A labeled, scroll-capped block of monospaced (or prose) text for a tool's input/result.
private struct ToolBlock: View {
    let label: String
    let text: String
    var mono: Bool = true
    var danger: Bool = false

    var body: some View {
        VStack(alignment: .leading, spacing: 2) {
            Text(label.uppercased()).font(.caption2.weight(.semibold)).foregroundStyle(.tertiary)
            Text(text)
                .font(mono ? .caption.monospaced() : .caption)
                .foregroundStyle(danger ? Palette.danger : .secondary)
                .textSelection(.enabled)
                .frame(maxWidth: .infinity, alignment: .leading)
                .frame(maxHeight: 220)
        }
    }
}

/// SF Symbols for the common tools + file-change kinds, so a glance reads the action.
enum ToolGlyph {
    static func icon(_ name: String) -> String {
        switch name {
        case "Bash", "Shell": return "terminal"
        case "Read": return "doc.text"
        case "Edit", "Write", "MultiEdit", "NotebookEdit": return "pencil"
        case "Grep", "Glob", "Search": return "magnifyingglass"
        case "WebFetch", "WebSearch": return "globe"
        case "Task", "Agent": return "person.2"
        case "TodoWrite": return "checklist"
        case "AskUserQuestion": return "questionmark.bubble"
        case HostCommandSummary.hostExecToolName: return "desktopcomputer"
        default: return "wrench.and.screwdriver"
        }
    }
    static func fileChange(_ kind: FileChange.ChangeKind) -> String {
        switch kind {
        case .add: return "plus.circle"
        case .update: return "pencil.circle"
        case .delete: return "minus.circle"
        case .rename: return "arrow.triangle.branch"
        }
    }
}
