//
//  JarvisView.swift
//  Confab
//
//  Main modal for managing Jarvis (oAI-Web) agents and usage.
//
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Copyright (C) 2026 Rune Olsen

import SwiftUI

// MARK: - Editor context (avoids .sheet timing bug)

private struct AgentEditContext: Identifiable {
    let id = UUID()
    let agent: JarvisAgent?  // nil → new
}

// MARK: - JarvisView

struct JarvisView: View {
    @Environment(\.dismiss) var dismiss
    @State private var selectedTab = 0
    @State private var agents: [JarvisAgent] = []
    @State private var runs: [String: [JarvisAgentRun]] = [:]
    @State private var usageStats: [JarvisUsageStat] = []
    @State private var credits: JarvisCreditsResponse? = nil
    @State private var queueStatus: JarvisQueueStatus? = nil
    @State private var isLoadingAgents = false
    @State private var isLoadingUsage = false
    @State private var selectedAgent: JarvisAgent? = nil
    @State private var editContext: AgentEditContext? = nil
    @State private var selectedRun: JarvisAgentRun? = nil
    @State private var errorMessage: String? = nil
    @State private var actionInProgress: Set<String> = []

    private let service = JarvisService.shared

    var body: some View {
        VStack(spacing: 0) {
            // Header
            HStack(alignment: .center) {
                Label("Jarvis", systemImage: "server.rack")
                    .font(.system(size: 18, weight: .bold))
                Spacer()
                if let qs = queueStatus {
                    queueStatusBadge(qs)
                }
                Button { dismiss() } label: {
                    Image(systemName: "xmark.circle.fill")
                        .font(.title2).foregroundStyle(.secondary)
                }
                .buttonStyle(.plain)
                .keyboardShortcut(.escape, modifiers: [])
            }
            .padding(.horizontal, 24)
            .padding(.top, 20)
            .padding(.bottom, 10)

            // Tab picker
            Picker("", selection: $selectedTab) {
                Text("Agents").tag(0)
                Text("Usage").tag(1)
            }
            .pickerStyle(.segmented)
            .padding(.horizontal, 24)
            .padding(.bottom, 10)

            Divider()

            if let err = errorMessage {
                HStack {
                    Image(systemName: "exclamationmark.triangle.fill").foregroundStyle(.orange)
                    Text(err).font(.callout).foregroundStyle(.secondary)
                    Spacer()
                    Button("Dismiss") { errorMessage = nil }.buttonStyle(.borderless)
                }
                .padding(.horizontal, 24)
                .padding(.vertical, 8)
                Divider()
            }

            switch selectedTab {
            case 0: agentsTab
            default: usageTab
            }
        }
        .frame(minWidth: 840, idealWidth: 960, minHeight: 560, idealHeight: 720)
        .task { await loadAll() }
        .sheet(item: $editContext) { ctx in
            JarvisAgentEditorSheet(agent: ctx.agent, onSave: { input in
                await saveAgent(existing: ctx.agent, input: input)
            })
        }
        .sheet(item: $selectedRun) { run in
            JarvisRunDetailSheet(run: run)
        }
    }

    // MARK: - Agents Tab

    private var agentsTab: some View {
        NavigationSplitView {
            agentList
        } detail: {
            if let agent = selectedAgent {
                agentDetailView(agent)
            } else {
                ContentUnavailableView("Select an Agent", systemImage: "server.rack",
                    description: Text("Choose an agent from the list to view details and run history"))
            }
        }
        .navigationSplitViewStyle(.balanced)
    }

    private var agentList: some View {
        VStack(spacing: 0) {
            // Toolbar
            HStack(spacing: 8) {
                Button {
                    editContext = AgentEditContext(agent: nil)
                } label: {
                    Label("New Agent", systemImage: "plus")
                }
                .buttonStyle(.bordered)
                .controlSize(.small)

                Spacer()

                if isLoadingAgents {
                    ProgressView().scaleEffect(0.7).frame(width: 14, height: 14)
                }

                Button { Task { await loadAgents() } } label: {
                    Image(systemName: "arrow.clockwise")
                }
                .buttonStyle(.borderless)
                .help("Refresh agents")

                // Queue control
                if let qs = queueStatus {
                    Button {
                        Task {
                            do {
                                if qs.paused == true { try await service.resumeAll() }
                                else { try await service.pauseAll() }
                                await loadQueueStatus()
                            } catch { errorMessage = error.localizedDescription }
                        }
                    } label: {
                        Image(systemName: qs.paused == true ? "play.fill" : "pause.fill")
                    }
                    .buttonStyle(.borderless)
                    .help(qs.paused == true ? "Resume queue" : "Pause queue")
                }
            }
            .padding(.horizontal, 12)
            .padding(.vertical, 8)

            Divider()

            if agents.isEmpty && !isLoadingAgents {
                ContentUnavailableView("No Agents", systemImage: "server.rack")
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            } else {
                List(agents, id: \.id, selection: $selectedAgent) { agent in
                    agentRow(agent)
                        .tag(agent)
                }
                .listStyle(.plain)
            }
        }
    }

    private func agentRow(_ agent: JarvisAgent) -> some View {
        HStack(spacing: 8) {
            // Status dot
            Circle()
                .fill(agent.isRunning == true ? Color.green : Color.secondary.opacity(0.4))
                .frame(width: 8, height: 8)

            VStack(alignment: .leading, spacing: 2) {
                Text(agent.name)
                    .font(.system(size: 13, weight: .medium))
                    .lineLimit(1)
                if let schedule = agent.schedule, !schedule.isEmpty {
                    Text(schedule)
                        .font(.caption2)
                        .foregroundStyle(.secondary)
                        .lineLimit(1)
                }
            }
            .frame(maxWidth: .infinity, alignment: .leading)

            // Enable toggle
            Toggle("", isOn: Binding(
                get: { agent.enabled },
                set: { _ in Task { await toggleAgent(agent) } }
            ))
            .toggleStyle(.switch)
            .controlSize(.mini)
            .labelsHidden()

            // Run / Stop button
            if agent.isRunning == true {
                Button {
                    Task { await stopAgent(agent) }
                } label: {
                    Image(systemName: "stop.fill")
                        .font(.system(size: 11))
                        .foregroundStyle(.red)
                }
                .buttonStyle(.borderless)
                .disabled(actionInProgress.contains(agent.id))
                .help("Stop agent")
            } else {
                Button {
                    Task { await runAgent(agent) }
                } label: {
                    Image(systemName: actionInProgress.contains(agent.id) ? "ellipsis" : "play.fill")
                        .font(.system(size: 11))
                        .foregroundStyle(.green)
                }
                .buttonStyle(.borderless)
                .disabled(actionInProgress.contains(agent.id) || !agent.enabled)
                .help("Run agent now")
            }
        }
        .padding(.vertical, 2)
        .contentShape(Rectangle())
    }

    @ViewBuilder
    private func agentDetailView(_ agent: JarvisAgent) -> some View {
        VStack(alignment: .leading, spacing: 0) {
            // Agent info header
            VStack(alignment: .leading, spacing: 12) {
                HStack {
                    VStack(alignment: .leading, spacing: 4) {
                        HStack(spacing: 8) {
                            Circle()
                                .fill(agent.isRunning == true ? Color.green : Color.secondary.opacity(0.4))
                                .frame(width: 10, height: 10)
                            Text(agent.name)
                                .font(.system(size: 16, weight: .semibold))
                            if !agent.enabled {
                                Text("Disabled")
                                    .font(.caption)
                                    .padding(.horizontal, 6).padding(.vertical, 2)
                                    .background(Color.orange.opacity(0.15))
                                    .foregroundStyle(.orange)
                                    .cornerRadius(4)
                            }
                        }
                        Text(agent.model)
                            .font(.caption)
                            .foregroundStyle(.secondary)
                    }
                    Spacer()
                    Button {
                        editContext = AgentEditContext(agent: agent)
                    } label: {
                        Label("Edit", systemImage: "pencil")
                    }
                    .buttonStyle(.bordered)
                    .controlSize(.small)

                    Button(role: .destructive) {
                        Task { await deleteAgent(agent) }
                    } label: {
                        Image(systemName: "trash")
                    }
                    .buttonStyle(.bordered)
                    .controlSize(.small)
                }

                if !agent.description.isEmpty {
                    Text(agent.description)
                        .font(.callout)
                        .foregroundStyle(.secondary)
                }

                if let schedule = agent.schedule, !schedule.isEmpty {
                    Label(schedule, systemImage: "clock")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
            .padding(16)

            Divider()

            // Prompt preview
            DisclosureGroup("Prompt") {
                ScrollView {
                    Text(agent.prompt)
                        .font(.system(size: 12, design: .monospaced))
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .textSelection(.enabled)
                        .padding(8)
                }
                .frame(maxHeight: 120)
                .background(Color.gray.opacity(0.06))
                .cornerRadius(6)
                .padding(.horizontal, 16)
                .padding(.bottom, 8)
            }
            .padding(.horizontal, 16)
            .padding(.top, 8)

            Divider()

            // Run history
            Text("Run History")
                .font(.system(size: 13, weight: .semibold))
                .foregroundStyle(.secondary)
                .padding(.horizontal, 16)
                .padding(.top, 10)
                .padding(.bottom, 4)

            let agentRuns = runs[agent.id] ?? []
            if agentRuns.isEmpty {
                Text("No runs yet")
                    .font(.callout)
                    .foregroundStyle(.secondary)
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            } else {
                List(agentRuns) { run in
                    RunHistoryRow(run: run)
                        .contentShape(Rectangle())
                        .onTapGesture { selectedRun = run }
                }
                .listStyle(.plain)
            }
        }
        .task(id: agent.id) {
            await loadRuns(for: agent)
        }
    }

    // MARK: - Usage Tab

    private var usageTab: some View {
        VStack(spacing: 0) {
            // Credits card
            if let creds = credits, let balance = creds.remainingBalance {
                HStack(spacing: 16) {
                    VStack(alignment: .leading, spacing: 4) {
                        Text("OpenRouter Balance")
                            .font(.caption)
                            .foregroundStyle(.secondary)
                        Text(String(format: "$%.4f", balance))
                            .font(.system(size: 22, weight: .semibold, design: .monospaced))
                            .foregroundStyle(balance < 1.0 ? .orange : .primary)
                    }
                    Spacer()
                    if let total = creds.totalCredits {
                        VStack(alignment: .trailing, spacing: 4) {
                            Text("Total Credits")
                                .font(.caption).foregroundStyle(.secondary)
                            Text(String(format: "$%.2f", total))
                                .font(.callout).foregroundStyle(.secondary)
                        }
                    }
                    if let used = creds.totalUsage {
                        VStack(alignment: .trailing, spacing: 4) {
                            Text("Total Used")
                                .font(.caption).foregroundStyle(.secondary)
                            Text(String(format: "$%.4f", used))
                                .font(.callout).foregroundStyle(.secondary)
                        }
                    }
                }
                .padding(16)
                .background(Color.blue.opacity(0.06))
                .cornerRadius(10)
                .padding(.horizontal, 16)
                .padding(.top, 12)
                .padding(.bottom, 8)
            }

            if isLoadingUsage {
                ProgressView("Loading usage…")
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            } else if usageStats.isEmpty {
                ContentUnavailableView("No Usage Data", systemImage: "chart.bar",
                    description: Text("Run some agents to see usage statistics"))
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            } else {
                // Header row
                HStack {
                    Text("Agent")
                        .frame(maxWidth: .infinity, alignment: .leading)
                    Text("Runs")
                        .frame(width: 50, alignment: .trailing)
                    Text("Input")
                        .frame(width: 80, alignment: .trailing)
                    Text("Output")
                        .frame(width: 80, alignment: .trailing)
                    Text("Cost")
                        .frame(width: 80, alignment: .trailing)
                }
                .font(.caption.weight(.semibold))
                .foregroundStyle(.secondary)
                .padding(.horizontal, 16)
                .padding(.vertical, 6)

                Divider()

                ScrollView {
                    VStack(spacing: 0) {
                        ForEach(usageStats) { stat in
                            usageRow(stat)
                            Divider().padding(.leading, 16)
                        }

                        // Totals row
                        let totalRuns = usageStats.compactMap(\.runCount).reduce(0, +)
                        let totalInput = usageStats.compactMap(\.totalInputTokens).reduce(0, +)
                        let totalOutput = usageStats.compactMap(\.totalOutputTokens).reduce(0, +)
                        let totalCost = usageStats.compactMap(\.totalCostUsd).reduce(0, +)
                        HStack {
                            Text("Total")
                                .font(.system(size: 13, weight: .semibold))
                                .frame(maxWidth: .infinity, alignment: .leading)
                            Text("\(totalRuns)")
                                .frame(width: 50, alignment: .trailing)
                                .font(.system(size: 13, weight: .semibold))
                            Text(formatTokens(totalInput))
                                .frame(width: 80, alignment: .trailing)
                                .font(.system(size: 13, weight: .semibold))
                            Text(formatTokens(totalOutput))
                                .frame(width: 80, alignment: .trailing)
                                .font(.system(size: 13, weight: .semibold))
                            Text(String(format: "$%.4f", totalCost))
                                .frame(width: 80, alignment: .trailing)
                                .font(.system(size: 13, weight: .semibold, design: .monospaced))
                        }
                        .padding(.horizontal, 16)
                        .padding(.vertical, 10)
                        .background(Color.gray.opacity(0.07))
                    }
                }

                HStack {
                    Spacer()
                    Button { Task { await loadUsage() } } label: {
                        Label("Refresh", systemImage: "arrow.clockwise")
                    }
                    .buttonStyle(.borderless)
                    .font(.caption)
                    .foregroundStyle(.secondary)
                }
                .padding(.horizontal, 16)
                .padding(.vertical, 6)
            }
        }
    }

    private func usageRow(_ stat: JarvisUsageStat) -> some View {
        HStack {
            Text(stat.displayName)
                .frame(maxWidth: .infinity, alignment: .leading)
                .lineLimit(1)
            Text("\(stat.runCount ?? 0)")
                .frame(width: 50, alignment: .trailing)
                .foregroundStyle(.secondary)
            Text(formatTokens(stat.totalInputTokens ?? 0))
                .frame(width: 80, alignment: .trailing)
                .foregroundStyle(.secondary)
            Text(formatTokens(stat.totalOutputTokens ?? 0))
                .frame(width: 80, alignment: .trailing)
                .foregroundStyle(.secondary)
            Text(stat.totalCostUsd.map { String(format: "$%.4f", $0) } ?? "—")
                .frame(width: 80, alignment: .trailing)
                .font(.system(size: 13, design: .monospaced))
        }
        .font(.system(size: 13))
        .padding(.horizontal, 16)
        .padding(.vertical, 8)
    }

    // MARK: - Queue Status Badge

    private func queueStatusBadge(_ qs: JarvisQueueStatus) -> some View {
        HStack(spacing: 6) {
            if qs.paused == true {
                Label("Paused", systemImage: "pause.fill")
                    .foregroundStyle(.orange)
            } else if let running = qs.runningCount, running > 0 {
                Label("^[\(running) running](inflect: true)", systemImage: "circle.fill")
                    .foregroundStyle(.green)
            } else {
                Label("Idle", systemImage: "checkmark.circle")
                    .foregroundStyle(.secondary)
            }
        }
        .font(.caption)
        .padding(.horizontal, 10)
        .padding(.vertical, 4)
        .background(Color.gray.opacity(0.1))
        .cornerRadius(6)
    }

    // MARK: - Data Loading

    private func loadAll() async {
        await withTaskGroup(of: Void.self) { group in
            group.addTask { await self.loadAgents() }
            group.addTask { await self.loadUsage() }
            group.addTask { await self.loadQueueStatus() }
        }
    }

    private func loadAgents() async {
        isLoadingAgents = true
        do {
            let fetched = try await service.listAgents()
            agents = fetched
            if let current = selectedAgent {
                selectedAgent = fetched.first(where: { $0.id == current.id })
            }
        } catch {
            errorMessage = error.localizedDescription
        }
        isLoadingAgents = false
    }

    private func loadRuns(for agent: JarvisAgent) async {
        do {
            let fetched = try await service.agentRuns(id: agent.id)
            runs[agent.id] = fetched
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    private func loadUsage() async {
        isLoadingUsage = true
        do {
            async let statsTask = service.usage()
            async let creditsTask = service.credits()
            usageStats = try await statsTask
            credits = try? await creditsTask
        } catch {
            errorMessage = error.localizedDescription
        }
        isLoadingUsage = false
    }

    private func loadQueueStatus() async {
        queueStatus = try? await service.queueStatus()
    }

    // MARK: - Agent Actions

    private func toggleAgent(_ agent: JarvisAgent) async {
        do {
            let updated = try await service.toggleAgent(id: agent.id)
            updateAgent(updated)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    private func runAgent(_ agent: JarvisAgent) async {
        actionInProgress.insert(agent.id)
        do {
            try await service.runAgent(id: agent.id)
            try? await Task.sleep(for: .seconds(1))
            await loadAgents()
        } catch {
            errorMessage = error.localizedDescription
        }
        actionInProgress.remove(agent.id)
    }

    private func stopAgent(_ agent: JarvisAgent) async {
        do {
            try await service.stopAgent(id: agent.id)
            try? await Task.sleep(for: .seconds(1))
            await loadAgents()
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    private func deleteAgent(_ agent: JarvisAgent) async {
        do {
            try await service.deleteAgent(id: agent.id)
            agents.removeAll { $0.id == agent.id }
            if selectedAgent?.id == agent.id { selectedAgent = nil }
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    private func saveAgent(existing: JarvisAgent?, input: JarvisAgentInput) async {
        do {
            let result: JarvisAgent
            if let existing {
                result = try await service.updateAgent(id: existing.id, input)
            } else {
                result = try await service.createAgent(input)
            }
            updateAgent(result)
            selectedAgent = result
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    private func updateAgent(_ updated: JarvisAgent) {
        if let idx = agents.firstIndex(where: { $0.id == updated.id }) {
            agents[idx] = updated
        } else {
            agents.append(updated)
        }
        if selectedAgent?.id == updated.id {
            selectedAgent = updated
        }
    }

    // MARK: - Formatting

    private func formatTokens(_ n: Int) -> String {
        if n >= 1_000_000 { return String(format: "%.1fM", Double(n) / 1_000_000) }
        if n >= 1_000 { return String(format: "%.1fK", Double(n) / 1_000) }
        return "\(n)"
    }
}

// MARK: - Run History Row

private struct RunHistoryRow: View {
    let run: JarvisAgentRun

    var body: some View {
        HStack(spacing: 8) {
            statusIcon
            VStack(alignment: .leading, spacing: 1) {
                HStack(spacing: 6) {
                    Text(run.formattedStarted)
                        .font(.system(size: 12))
                    if let dur = run.formattedDuration {
                        Text("· \(dur)")
                            .font(.system(size: 12))
                            .foregroundStyle(.secondary)
                    }
                }
                HStack(spacing: 8) {
                    if run.totalTokens > 0 {
                        Text("^[\(run.totalTokens) token](inflect: true)")
                            .font(.caption2)
                            .foregroundStyle(.secondary)
                    }
                    if let cost = run.costUsd, cost > 0 {
                        Text(String(format: "$%.5f", cost))
                            .font(.caption2.monospaced())
                            .foregroundStyle(.secondary)
                    }
                }
            }
            Spacer()
            // Tapping the row (wired by the caller) opens JarvisRunDetailSheet with the full,
            // untruncated output/error — this used to be an inline chevron-expand capped at
            // lineLimit(20), which isn't "complete" for long output.
            if run.output != nil || run.error != nil {
                Image(systemName: "chevron.right")
                    .font(.caption2)
                    .foregroundStyle(.tertiary)
            }
        }
        .padding(.vertical, 4)
    }

    @ViewBuilder
    private var statusIcon: some View {
        JarvisRunStatusIcon(status: run.status, size: 14)
    }
}

/// Shared between RunHistoryRow and JarvisRunDetailSheet so the two views can't drift apart.
fileprivate struct JarvisRunStatusIcon: View {
    let status: String
    var size: CGFloat = 14

    var body: some View {
        switch status {
        case "running":
            ProgressView().scaleEffect(size / 24).frame(width: size, height: size)
        case "completed", "success":
            Image(systemName: "checkmark.circle.fill")
                .font(.system(size: size))
                .foregroundStyle(.green)
        case "failed", "error":
            Image(systemName: "xmark.circle.fill")
                .font(.system(size: size))
                .foregroundStyle(.red)
        case "stopped":
            Image(systemName: "stop.circle.fill")
                .font(.system(size: size))
                .foregroundStyle(.orange)
        default:
            Image(systemName: "circle")
                .font(.system(size: size))
                .foregroundStyle(.secondary)
        }
    }
}

// MARK: - Run Detail Sheet

/// Full, untruncated view of a single run — opened by tapping a row in the run history list.
/// Output/error use an explicit Copy button rather than .textSelection(.enabled): a multi-line
/// selectable Text can grab real AppKit first-responder status, and Escape then gets consumed by
/// its own cancelOperation: handling instead of dismissing the sheet (silent beep, no log) — see
/// the identical bug fixed in ModelInfoView.
struct JarvisRunDetailSheet: View {
    let run: JarvisAgentRun

    @Environment(\.dismiss) var dismiss
    @State private var showOutputCopied = false
    @State private var showErrorCopied = false

    var body: some View {
        VStack(spacing: 0) {
            HStack {
                Text("Run Details")
                    .font(.system(size: 18, weight: .bold))
                Spacer()
                JarvisRunStatusIcon(status: run.status, size: 16)
                Text(run.status.capitalized)
                    .font(.system(size: 13, weight: .medium))
                    .foregroundStyle(.secondary)
                Button { dismiss() } label: {
                    Image(systemName: "xmark.circle.fill")
                        .font(.title2)
                        .foregroundStyle(.secondary)
                }
                .buttonStyle(.plain)
                .padding(.leading, 8)
                .keyboardShortcut(.escape, modifiers: [])
            }
            .padding(.horizontal, 24)
            .padding(.top, 20)
            .padding(.bottom, 12)

            Divider()

            ScrollView {
                VStack(alignment: .leading, spacing: 16) {
                    VStack(alignment: .leading, spacing: 6) {
                        detailRow("Started", run.formattedStarted)
                        if let dur = run.formattedDuration {
                            detailRow("Duration", dur)
                        }
                        if let trigger = run.triggerType {
                            detailRow("Trigger", trigger.capitalized)
                        }
                        if run.totalTokens > 0 {
                            detailRow("Tokens", "\(run.totalTokens.formatted()) (\((run.inputTokens ?? 0).formatted()) in / \((run.outputTokens ?? 0).formatted()) out)")
                        }
                        if let cost = run.costUsd, cost > 0 {
                            detailRow("Cost", String(format: "$%.5f", cost))
                        }
                    }

                    if let err = run.error {
                        Divider()
                        outputBlock(title: "Error", text: err, color: .red, showCopied: $showErrorCopied)
                    }
                    if let out = run.output {
                        Divider()
                        outputBlock(title: "Output", text: out, color: .primary, showCopied: $showOutputCopied)
                    }
                    if run.output == nil && run.error == nil {
                        Text("No output for this run.")
                            .font(.callout)
                            .foregroundStyle(.secondary)
                    }
                }
                .padding(24)
            }
        }
        .frame(minWidth: 600, idealWidth: 700, minHeight: 450, idealHeight: 620)
        .onExitCommand { dismiss() }
    }

    private func detailRow(_ label: LocalizedStringKey, _ value: String) -> some View {
        HStack {
            Text(label)
                .font(.system(size: 13))
                .foregroundStyle(.secondary)
                .frame(width: 90, alignment: .leading)
            Text(value)
                .font(.system(size: 13))
            Spacer()
        }
    }

    @ViewBuilder
    private func outputBlock(title: LocalizedStringKey, text: String, color: Color, showCopied: Binding<Bool>) -> some View {
        VStack(alignment: .leading, spacing: 6) {
            HStack(spacing: 6) {
                Text(title)
                    .font(.subheadline.weight(.medium))
                    .foregroundColor(.secondary)
                Spacer()
                Button(action: { copy(text, showCopied: showCopied) }) {
                    HStack(spacing: 3) {
                        Image(systemName: showCopied.wrappedValue ? "checkmark" : "doc.on.doc")
                            .font(.system(size: 11))
                        if showCopied.wrappedValue {
                            Text("Copied!")
                                .font(.system(size: 11))
                        }
                    }
                    .foregroundColor(showCopied.wrappedValue ? .green : .secondary)
                }
                .buttonStyle(.plain)
            }
            Text(text)
                .font(.system(size: 12, design: .monospaced))
                .foregroundStyle(color)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(10)
                .background(color.opacity(0.06))
                .cornerRadius(6)
        }
    }

    private func copy(_ text: String, showCopied: Binding<Bool>) {
        let pasteboard = NSPasteboard.general
        pasteboard.clearContents()
        pasteboard.setString(text, forType: .string)
        withAnimation {
            showCopied.wrappedValue = true
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
            withAnimation {
                showCopied.wrappedValue = false
            }
        }
    }
}

// MARK: - Agent Editor Sheet

struct JarvisAgentEditorSheet: View {
    let agent: JarvisAgent?
    let onSave: (JarvisAgentInput) async -> Void

    @Environment(\.dismiss) var dismiss
    @State private var name: String
    @State private var description: String
    @State private var model: String
    @State private var prompt: String
    @State private var schedule: String
    @State private var enabled: Bool
    @State private var maxToolCalls: String
    @State private var isSaving = false

    init(agent: JarvisAgent?, onSave: @escaping (JarvisAgentInput) async -> Void) {
        self.agent = agent
        self.onSave = onSave
        _name = State(initialValue: agent?.name ?? "")
        _description = State(initialValue: agent?.description ?? "")
        _model = State(initialValue: agent?.model ?? "")
        _prompt = State(initialValue: agent?.prompt ?? "")
        _schedule = State(initialValue: agent?.schedule ?? "")
        _enabled = State(initialValue: agent?.enabled ?? true)
        _maxToolCalls = State(initialValue: agent.flatMap(\.maxToolCalls).map(String.init) ?? "")
    }

    var isValid: Bool { !name.trimmingCharacters(in: .whitespaces).isEmpty && !model.trimmingCharacters(in: .whitespaces).isEmpty }

    var body: some View {
        VStack(spacing: 0) {
            // Header
            HStack {
                Text(agent == nil ? "New Agent" : "Edit Agent")
                    .font(.system(size: 16, weight: .semibold))
                Spacer()
                Button { dismiss() } label: {
                    Image(systemName: "xmark.circle.fill")
                        .font(.title3).foregroundStyle(.secondary)
                }
                .buttonStyle(.plain)
            }
            .padding(.horizontal, 20)
            .padding(.top, 16)
            .padding(.bottom, 12)

            Divider()

            ScrollView {
                VStack(alignment: .leading, spacing: 16) {
                    Group {
                        editorField("Name") {
                            TextField("Agent name", text: $name)
                                .textFieldStyle(.roundedBorder)
                        }
                        editorField("Description") {
                            TextField("Optional description", text: $description)
                                .textFieldStyle(.roundedBorder)
                        }
                        editorField("Model") {
                            TextField("e.g. anthropic/claude-sonnet-4-5", text: $model)
                                .textFieldStyle(.roundedBorder)
                        }
                        editorField("Schedule") {
                            TextField("Cron expression (e.g. 0 * * * *)", text: $schedule)
                                .textFieldStyle(.roundedBorder)
                        }
                        editorField("Max Tool Calls") {
                            TextField("Leave blank for default", text: $maxToolCalls)
                                .textFieldStyle(.roundedBorder)
                                .frame(maxWidth: 160)
                        }
                        editorField("Enabled") {
                            Toggle("", isOn: $enabled)
                                .toggleStyle(.switch)
                                .labelsHidden()
                        }
                    }

                    VStack(alignment: .leading, spacing: 6) {
                        Text("Prompt")
                            .font(.system(size: 13, weight: .medium))
                        TextEditor(text: $prompt)
                            .font(.system(size: 13, design: .monospaced))
                            .frame(minHeight: 160)
                            .overlay(
                                RoundedRectangle(cornerRadius: 6)
                                    .strokeBorder(Color.gray.opacity(0.3))
                            )
                    }
                }
                .padding(20)
            }

            Divider()

            HStack(spacing: 10) {
                Spacer()
                Button("Cancel") { dismiss() }
                    .keyboardShortcut(.escape, modifiers: [])
                Button {
                    Task {
                        isSaving = true
                        let input = JarvisAgentInput(
                            name: name.trimmingCharacters(in: .whitespaces),
                            prompt: prompt,
                            model: model.trimmingCharacters(in: .whitespaces),
                            description: description,
                            enabled: enabled,
                            schedule: schedule.isEmpty ? nil : schedule,
                            maxToolCalls: Int(maxToolCalls)
                        )
                        await onSave(input)
                        isSaving = false
                        dismiss()
                    }
                } label: {
                    if isSaving {
                        ProgressView().scaleEffect(0.8).frame(width: 16, height: 16)
                    } else {
                        Text(agent == nil ? "Create" : "Save")
                    }
                }
                .buttonStyle(.borderedProminent)
                .disabled(!isValid || isSaving)
                .keyboardShortcut(.return, modifiers: .command)
            }
            .padding(.horizontal, 20)
            .padding(.vertical, 12)
        }
        .frame(minWidth: 560, idealWidth: 620, minHeight: 560, idealHeight: 700)
    }

    private func editorField<Content: View>(_ label: LocalizedStringKey, @ViewBuilder content: () -> Content) -> some View {
        HStack(alignment: .firstTextBaseline, spacing: 12) {
            Text(label)
                .font(.system(size: 13))
                .frame(width: 100, alignment: .trailing)
            content()
        }
    }
}
