import { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { apiFetch } from '@core/lib/api'
import { useAppPath } from '@core/hooks/useAppPath'
import { Card, CardContent, CardHeader, CardTitle } from '@core/components/ui/card'
import { Button } from '@core/components/ui/button'
import { Badge } from '@core/components/ui/badge'
import { Skeleton } from '@core/components/ui/skeleton'

import { Input } from '@core/components/ui/input'
import { Label } from '@core/components/ui/label'
import { Switch } from '@core/components/ui/switch'
import {
  Play,
  CheckCircle2,
  XCircle,
  AlertTriangle,
  Clock,
  FileCode2,
  Code2,
  GitPullRequest,
  ChevronRight,
  RefreshCw,
  Zap,
  Sparkles,
  GitGraph,
  Settings2,
  Save,
  DollarSign,
} from 'lucide-react'

interface Project {
  id: string
  title: string
  data: {
    repo_path: string
    ingestion_status: string
    file_count: number
    chunk_count: number
    total_ingestion_cost_usd: number
    last_ingested_at: string | null
    globs?: string[]
    exclude_globs?: string[]
  }
  stats?: {
    file_count: number
    chunk_count: number
    pending_review_count: number
  }
}

interface ChunkProposal {
  id: string
  title: string
  data: {
    file_path: string
    proposed_name: string
    line_start: number
    line_end: number
    chunk_type: string
    confidence_score: number
    review_status: string
    code_preview: string
    confidence_reasons: string
    existing_marker_match: boolean
    existing_marker_name: string
  }
}

interface PassConfig {
  repo_path: string
  globs: string
  exclude_globs: string
  force_reprocess: boolean
}

const PASS_STEPS = [
  { key: 'pass1',  label: 'Pass 1 — Discover Files', icon: FileCode2,      description: 'Walk the repo, register cil_file items, compute hashes' },
  { key: 'pass2',  label: 'Pass 2 — AST Chunk',      icon: Code2,          description: 'Parse files with AST, propose chunk boundaries, write high-confidence markers' },
  { key: 'review', label: 'Review Queue',             icon: GitPullRequest, description: 'Review low-confidence boundary proposals before continuing' },
  { key: 'pass3',  label: 'Pass 3 — AI Document',    icon: Sparkles,       description: 'Use LLM to fill macro / micro / contract fields — do this before embedding so vectors are rich' },
  { key: 'pass4',  label: 'Pass 4 — Embed Chunks',   icon: Zap,            description: 'Generate semantic + structural + contract vector embeddings using the documented fields' },
  { key: 'pass4b', label: 'Pass 5 — Call Map',       icon: GitGraph,       description: 'Extract import + call edges, write cil_imports, cil_calls, cil_tested_by links' },
  { key: 'pass5',  label: 'Pass 6 — Config Extract', icon: Settings2,      description: 'Scan config files, create cil_config items, write cil_configures links' },
]

function ProposalRow({ proposal, selected, onToggle }: {
  proposal: ChunkProposal
  selected: boolean
  onToggle: (id: string) => void
}) {
  const d = proposal.data
  return (
    <div
      className={`flex items-start gap-3 p-3 rounded-lg border cursor-pointer transition-colors ${
        selected ? 'bg-green-50 border-green-300 dark:bg-green-950/20' : 'bg-card border-border hover:bg-muted/40'
      }`}
      onClick={() => onToggle(proposal.id)}
    >
      {/* Checkbox */}
      <div className={`mt-0.5 h-4 w-4 rounded border-2 flex items-center justify-center shrink-0 ${
        selected ? 'bg-green-500 border-green-500' : 'border-muted-foreground'
      }`}>
        {selected && <CheckCircle2 className="h-3 w-3 text-white" />}
      </div>

      {/* Content */}
      <div className="flex-1 min-w-0 space-y-1">
        <div className="flex items-center gap-2 flex-wrap">
          <span className="text-sm font-medium">{d.proposed_name || proposal.title}</span>
          <Badge variant="outline" className="text-xs">{d.chunk_type || 'unknown'}</Badge>
          <Badge variant="secondary" className="text-xs">
            {Math.round((d.confidence_score || 0) * 100)}% confidence
          </Badge>
          {d.existing_marker_match && (
            <span className="text-xs text-blue-600 dark:text-blue-400 flex items-center gap-1">
              <CheckCircle2 className="h-3 w-3" />
              matches marker
            </span>
          )}
        </div>
        <p className="text-xs text-muted-foreground font-mono truncate">
          {d.file_path} · L{d.line_start}–{d.line_end}
        </p>
        {d.confidence_reasons && (
          <p className="text-xs text-muted-foreground">{d.confidence_reasons}</p>
        )}
        {d.code_preview && (
          <pre className="text-xs bg-muted rounded p-2 overflow-auto max-h-24 font-mono">
            {d.code_preview}
          </pre>
        )}
      </div>
    </div>
  )
}

export default function CILIngestionPage() {
  const { id: projectId } = useParams<{ id: string }>()
  const appPath = useAppPath()
  const [project, setProject] = useState<Project | null>(null)
  const [proposals, setProposals] = useState<ChunkProposal[]>([])
  const [selectedProposalIds, setSelectedProposalIds] = useState<Set<string>>(new Set())
  const [bulkProcessing, setBulkProcessing] = useState(false)
  const [loadingProject, setLoadingProject] = useState(true)
  const [loadingProposals, setLoadingProposals] = useState(true)

  const [runningPass, setRunningPass] = useState<string | null>(null)
  const [passResults, setPassResults] = useState<Record<string, any>>({})
  const [passProgress, setPassProgress] = useState<Record<string, { done: number; total: number } | null>>({})
  const [passConfig, setPassConfig] = useState<PassConfig>({
    repo_path: '',
    globs: '**/*.ts,**/*.tsx,**/*.sql',
    exclude_globs: 'node_modules/**,.git/**,dist/**,build/**,.netlify/**,**/*.min.js,**/*.d.ts',
    force_reprocess: false,
  })
  const [ingestMode, setIngestMode] = useState<'batch' | 'file'>('batch')
  const [targetFilePath, setTargetFilePath] = useState('')
  const [savingConfig, setSavingConfig] = useState(false)
  const [configSaved, setConfigSaved] = useState(false)
  const [costEstimate, setCostEstimate] = useState<any>(null)
  const [loadingEstimate, setLoadingEstimate] = useState(false)
  const [embeddingEstimate, setEmbeddingEstimate] = useState<any>(null)
  const [loadingEmbeddingEstimate, setLoadingEmbeddingEstimate] = useState(false)

  useEffect(() => {
    if (!projectId) return
    apiFetch(`/api/custom_cil-project?action=get&id=${projectId}`)
      .then(r => r.json())
      .then(data => {
        const p = data?.project || data?.data?.project || null
        setProject(p)
        // Populate pass config from persisted project settings (always, using defaults if not yet saved)
        const DEFAULT_GLOBS = '**/*.ts,**/*.tsx,**/*.sql'
        const DEFAULT_EXCLUDES = 'node_modules/**,.git/**,dist/**,build/**,.netlify/**,**/*.min.js,**/*.d.ts'
        setPassConfig(c => ({
          ...c,
          repo_path: p?.data?.repo_path || '',
          globs: p?.data?.globs?.length ? p.data.globs.join(',') : DEFAULT_GLOBS,
          exclude_globs: p?.data?.exclude_globs?.length ? p.data.exclude_globs.join(',') : DEFAULT_EXCLUDES,
        }))
      })
      .catch(() => {})
      .finally(() => setLoadingProject(false))
  }, [projectId])

  const saveConfig = async () => {
    if (!projectId) return
    setSavingConfig(true)
    setConfigSaved(false)
    try {
      await apiFetch(`/api/custom_cil-project?action=update&id=${projectId}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          id: projectId,
          // Send only the fields we own — server merges into existing data
          data: {
            repo_path: passConfig.repo_path.trim(),
            globs: passConfig.globs.split(',').map(s => s.trim()).filter(Boolean),
            exclude_globs: passConfig.exclude_globs.split(',').map(s => s.trim()).filter(Boolean),
          },
        }),
      })
      setConfigSaved(true)
      setTimeout(() => setConfigSaved(false), 2000)
      // Refresh project state so UI reflects merged record
      apiFetch(`/api/custom_cil-project?action=get&id=${projectId}`)
        .then(r => r.json())
        .then(data => { const p = data?.project || data?.data?.project || null; if (p) setProject(p) })
        .catch(() => {})
    } catch {
      // silent — user can retry
    } finally {
      setSavingConfig(false)
    }
  }

  const fetchProposals = () => {
    if (!projectId) return
    setLoadingProposals(true)
    apiFetch(`/api/custom_cil-project?action=list_proposals&id=${projectId}&status=pending_review`)
      .then(r => r.json())
      .then(res => {
        // Framework wraps response in { data: { proposals: [...] } }
        setProposals(res?.data?.proposals || res?.proposals || [])
      })
      .catch(() => {})
      .finally(() => setLoadingProposals(false))
  }

  useEffect(() => { fetchProposals() }, [projectId])

  const fetchCostEstimate = () => {
    if (!projectId) return
    setLoadingEstimate(true)
    const fileParam = (ingestMode === 'file' && targetFilePath)
      ? `&file_path=${encodeURIComponent(targetFilePath)}`
      : ''
    apiFetch(`/api/custom_cil-doc?action=estimate&project_id=${projectId}${fileParam}`)
      .then(r => r.json())
      .then(res => setCostEstimate(res?.data || res || null))
      .catch(() => setCostEstimate(null))
      .finally(() => setLoadingEstimate(false))
  }

  const fetchEmbeddingEstimate = () => {
    if (!projectId) return
    setLoadingEmbeddingEstimate(true)
    const fileParam = (ingestMode === 'file' && targetFilePath)
      ? `&file_path=${encodeURIComponent(targetFilePath)}`
      : ''
    apiFetch(`/api/custom_cil-embeddings?action=estimate&project_id=${projectId}${fileParam}`)
      .then(r => r.json())
      .then(res => setEmbeddingEstimate(res?.data || res || null))
      .catch(() => setEmbeddingEstimate(null))
      .finally(() => setLoadingEmbeddingEstimate(false))
  }

  useEffect(() => {
    fetchCostEstimate()
    fetchEmbeddingEstimate()
  }, [projectId, ingestMode, targetFilePath])

  const runPass = async (pass: 'pass1' | 'pass2' | 'pass3' | 'pass4' | 'pass4b' | 'pass5') => {
    if (!projectId) return
    setRunningPass(pass)
    try {
      let res: Response
      if (pass === 'pass3') {
        // Paginated loop — 50 chunks per request, ~20 in parallel each, well within 30s
        const PAGE = 50
        let offset = 0
        let totalDocumented = 0, totalSkipped = 0, totalFailed = 0
        const allErrors: string[] = []
        setPassProgress(p => ({ ...p, pass3: { done: 0, total: 0 } }))

        while (true) {
          const pageRes = await apiFetch('/api/custom_cil-doc?action=document', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ project_id: projectId, limit: PAGE, offset, file_path: targetFilePath || undefined }),
          })
          const pageData = await pageRes.json()
          const d = pageData?.data || pageData
          totalDocumented += d.documented || 0
          totalSkipped    += d.skipped    || 0
          totalFailed     += d.failed     || 0
          if (d.errors?.length) allErrors.push(...d.errors)
          setPassProgress(p => ({ ...p, pass3: { done: d.done_total || offset + PAGE, total: d.total_chunks || d.total_to_document || 0 } }))
          if (!d.has_more) break
          offset = d.next_offset
        }

        setPassResults(prev => ({ ...prev, pass3: { documented: totalDocumented, skipped: totalSkipped, failed: totalFailed, errors: allErrors } }))
        setPassProgress(p => ({ ...p, pass3: null }))
        fetchCostEstimate()
        setRunningPass(null)
        return // skip the generic result-setting below
      } else if (pass === 'pass4') {
        // Paginated loop — 200 chunks per request, parallel OpenAI batches within each
        const PAGE = 200
        let offset = 0
        let totalCreated = 0, totalSkipped = 0
        const allErrors: string[] = []
        setPassProgress(p => ({ ...p, pass4: { done: 0, total: 0 } }))

        while (true) {
          const pageRes = await apiFetch('/api/custom_cil-embeddings?action=embed_approved', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ project_id: projectId, limit: PAGE, offset, file_path: targetFilePath || undefined }),
          })
          const pageData = await pageRes.json()
          const d = pageData?.data || pageData
          totalCreated += d.embeddings_created || 0
          totalSkipped += d.embeddings_skipped || 0
          if (d.errors?.length) allErrors.push(...d.errors)
          setPassProgress(p => ({ ...p, pass4: { done: d.done_total || offset + PAGE, total: d.total || 0 } }))
          if (!d.has_more) break
          offset = d.next_offset
        }

        setPassResults(prev => ({ ...prev, pass4: { embeddings_created: totalCreated, embeddings_skipped: totalSkipped, errors: allErrors } }))
        setPassProgress(p => ({ ...p, pass4: null }))
        fetchEmbeddingEstimate()
        setRunningPass(null)
        return
      } else if (pass === 'pass4b') {
        // Paginated loop — 100 files per request
        const PAGE = 100
        let offset = 0
        let totalImports = 0, totalCalls = 0, totalExports = 0
        const allErrors: string[] = []
        setPassProgress(p => ({ ...p, pass4b: { done: 0, total: 0 } }))

        while (true) {
          const pageRes = await apiFetch('/api/custom_cil-callmap?action=map_project', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ project_id: projectId, limit: PAGE, offset, file_path: targetFilePath || undefined }),
          })
          const pageData = await pageRes.json()
          const d = pageData?.data || pageData
          totalImports += d.imports_created || 0
          totalCalls   += d.calls_created   || 0
          totalExports += d.exports_created || 0
          if (d.errors?.length) allErrors.push(...d.errors)
          setPassProgress(p => ({ ...p, pass4b: { done: d.done_total || offset + PAGE, total: d.total_files || 0 } }))
          if (!d.has_more) break
          offset = d.next_offset
        }

        setPassResults(prev => ({ ...prev, pass4b: { imports_created: totalImports, calls_created: totalCalls, exports_created: totalExports, errors: allErrors } }))
        setPassProgress(p => ({ ...p, pass4b: null }))
        setRunningPass(null)
        return
      } else if (pass === 'pass5') {
        res = await apiFetch('/api/custom_cil-config?action=run', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ project_id: projectId, file_path: targetFilePath || undefined }),
        })
      } else {
        res = await apiFetch(`/api/custom_cil-ingest?action=${pass}`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            project_id: projectId,
            globs: passConfig.globs.split(',').map(s => s.trim()).filter(Boolean),
            exclude_globs: passConfig.exclude_globs.split(',').map(s => s.trim()).filter(Boolean),
            force_reprocess: passConfig.force_reprocess,
            file_path: targetFilePath || undefined,
          }),
        })
      }
      const result = await res.json()
      setPassResults(prev => ({ ...prev, [pass]: result?.data || result }))
      fetchProposals()
      if (pass === 'pass2') { fetchCostEstimate(); fetchEmbeddingEstimate() }
    } catch (err) {
      setPassResults(prev => ({ ...prev, [pass]: { error: String(err) } }))
    } finally {
      setRunningPass(null)
    }
  }

  const toggleProposalSelection = (id: string) => {
    setSelectedProposalIds(prev => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }

  const toggleSelectAll = () => {
    if (selectedProposalIds.size === proposals.length) {
      setSelectedProposalIds(new Set())
    } else {
      setSelectedProposalIds(new Set(proposals.map(p => p.id)))
    }
  }

  const handleBulkApprove = async () => {
    if (!selectedProposalIds.size || !projectId) return
    setBulkProcessing(true)
    const ids = Array.from(selectedProposalIds)
    const repoPath = passConfig.repo_path || project?.data?.repo_path || ''
    await Promise.all(ids.map(proposalId =>
      apiFetch(`/api/custom_cil-ingest?action=promote_proposal`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ project_id: projectId, proposal_id: proposalId, repo_path: repoPath }),
      }).catch(() =>
        // Fallback: mark as approved without disk write
        apiFetch(`/api/admin-data?id=${proposalId}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ entity: 'items', data: { review_status: 'approved' } }),
        })
      )
    ))
    setSelectedProposalIds(new Set())
    setBulkProcessing(false)
    fetchProposals()
  }

  const handleBulkReject = async () => {
    if (!selectedProposalIds.size) return
    setBulkProcessing(true)
    const ids = Array.from(selectedProposalIds)
    await Promise.all(ids.map(proposalId =>
      apiFetch(`/api/admin-data?id=${proposalId}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ entity: 'items', data: { review_status: 'rejected' } }),
      })
    ))
    setSelectedProposalIds(new Set())
    setBulkProcessing(false)
    fetchProposals()
  }

  if (loadingProject) {
    return (
      <div className="p-6 space-y-4">
        <Skeleton className="h-8 w-64" />
        <Skeleton className="h-40 w-full" />
      </div>
    )
  }

  if (!project) {
    return (
      <div className="p-6 text-center text-muted-foreground">
        Project not found.
      </div>
    )
  }

  return (
    <div className="p-6 space-y-6">
      {/* Header */}
      <div>
        <div className="flex items-center gap-2 text-sm text-muted-foreground mb-1">
          <a href={appPath('/cil')} className="hover:underline">Code Intelligence</a>
          <ChevronRight className="h-3 w-3" />
          <span>{project.title}</span>
        </div>
        <div className="flex items-center justify-between gap-4 flex-wrap">
          <div>
            <h1 className="text-2xl font-bold">{project.title} — Ingestion</h1>
            <p className="text-xs text-muted-foreground font-mono mt-0.5">{project.data?.repo_path}</p>
          </div>
          <a
            href={appPath(`/cil/${projectId}/graph`)}
            className="inline-flex items-center gap-1.5 text-sm text-indigo-600 hover:text-indigo-700 hover:underline"
          >
            <GitGraph className="h-4 w-4" />
            View Graph
          </a>
        </div>
      </div>

      {/* Stats row */}
      <div className="grid grid-cols-4 gap-4">
        {[
          { label: 'Files', value: project.stats?.file_count ?? project.data?.file_count ?? 0 },
          { label: 'Chunks', value: project.stats?.chunk_count ?? project.data?.chunk_count ?? 0 },
          { label: 'Pending Review', value: proposals.length },
          { label: 'Cost', value: `$${(project.data?.total_ingestion_cost_usd ?? 0).toFixed(4)}` },
        ].map(s => (
          <Card key={s.label}>
            <CardContent className="pt-4 pb-3">
              <div className="text-2xl font-bold">{s.value}</div>
              <div className="text-xs text-muted-foreground">{s.label}</div>
            </CardContent>
          </Card>
        ))}
      </div>

      <div className="space-y-4">
          {/* Pass Config */}
          <Card>
            <CardHeader>
              <div className="flex items-center justify-between">
                <CardTitle className="text-sm">Pass Configuration</CardTitle>
                <Button
                  size="sm"
                  variant="outline"
                  onClick={saveConfig}
                  disabled={savingConfig}
                >
                  {configSaved ? (
                    <>
                      <CheckCircle2 className="h-3 w-3 mr-1 text-green-500" />
                      Saved
                    </>
                  ) : (
                    <>
                      <Save className="h-3 w-3 mr-1" />
                      {savingConfig ? 'Saving…' : 'Save'}
                    </>
                  )}
                </Button>
              </div>
            </CardHeader>
            <CardContent className="space-y-3">
              <div className="space-y-1">
                <Label className="text-xs font-semibold">Repository Path <span className="text-red-500">*</span></Label>
                <Input
                  value={passConfig.repo_path}
                  onChange={e => setPassConfig(c => ({ ...c, repo_path: e.target.value }))}
                  placeholder="/absolute/path/to/repo"
                  className="font-mono text-xs"
                />
                {!passConfig.repo_path && (
                  <p className="text-xs text-red-500">Required — absolute path to the repository on this machine</p>
                )}
              </div>
              <div className="space-y-1">
                <Label className="text-xs">File Globs (comma-separated)</Label>
                <Input
                  value={passConfig.globs}
                  onChange={e => setPassConfig(c => ({ ...c, globs: e.target.value }))}
                  placeholder="**/*.ts,**/*.tsx"
                  className="font-mono text-xs"
                />
              </div>
              <div className="space-y-1">
                <Label className="text-xs">Exclude Globs (comma-separated)</Label>
                <Input
                  value={passConfig.exclude_globs}
                  onChange={e => setPassConfig(c => ({ ...c, exclude_globs: e.target.value }))}
                  placeholder="node_modules/**,dist/**"
                  className="font-mono text-xs"
                />
                <p className="text-xs text-muted-foreground">
                  Patterns saved here are applied to all passes for this project.
                </p>
              </div>

              <div className="pt-2 border-t space-y-3">
                <div className="flex items-center justify-between">
                  <div className="space-y-0.5">
                    <Label className="text-xs font-semibold">Target</Label>
                    <p className="text-xs text-muted-foreground">
                      {ingestMode === 'batch'
                        ? 'Run passes across the whole project using the globs above.'
                        : 'Run passes against a single file or directory only.'}
                    </p>
                  </div>
                  <div className="flex items-center gap-2">
                    <span className={`text-xs ${ingestMode === 'batch' ? 'font-medium' : 'text-muted-foreground'}`}>Batch</span>
                    <Switch
                      checked={ingestMode === 'file'}
                      onCheckedChange={checked => setIngestMode(checked ? 'file' : 'batch')}
                    />
                    <span className={`text-xs ${ingestMode === 'file' ? 'font-medium' : 'text-muted-foreground'}`}>Single</span>
                  </div>
                </div>
                {ingestMode === 'file' && (
                  <div className="space-y-1">
                    <Label className="text-xs">Project-relative path</Label>
                    <Input
                      value={targetFilePath}
                      onChange={e => setTargetFilePath(e.target.value)}
                      placeholder=".framework/functions/_shared/principal.ts"
                      className="font-mono text-xs"
                    />
                    <p className="text-xs text-muted-foreground">
                      Path from the project root. Passes 1–6 will filter to this file/dir.
                    </p>
                  </div>
                )}
              </div>
            </CardContent>
          </Card>

          {/* Pass steps */}
          {PASS_STEPS.map((step, idx) => {
            // Review queue renders inline between AST chunk and AI Document
            if (step.key === 'review') {
              return (
                <Card key="review" className="overflow-hidden border-amber-200">
                  <CardHeader className="pb-2 bg-amber-50 dark:bg-amber-950/20">
                    <div className="flex items-center justify-between gap-3 flex-wrap">
                      <div className="flex items-center gap-2">
                        <div className="h-6 w-6 rounded-full bg-amber-100 dark:bg-amber-900 flex items-center justify-center text-xs font-bold text-amber-700">
                          {idx + 1}
                        </div>
                        <div>
                          <CardTitle className="text-sm">{step.label}</CardTitle>
                          <p className="text-xs text-muted-foreground">{step.description}</p>
                        </div>
                      </div>
                      <div className="flex items-center gap-2">
                        {selectedProposalIds.size > 0 && (
                          <>
                            <Button size="sm" variant="outline"
                              className="text-green-600 border-green-200 hover:bg-green-50"
                              onClick={handleBulkApprove} disabled={bulkProcessing}>
                              {bulkProcessing ? <RefreshCw className="h-3 w-3 mr-1 animate-spin" /> : <CheckCircle2 className="h-3 w-3 mr-1" />}
                              Approve {selectedProposalIds.size}
                            </Button>
                            <Button size="sm" variant="outline"
                              className="text-red-600 border-red-200 hover:bg-red-50"
                              onClick={handleBulkReject} disabled={bulkProcessing}>
                              {bulkProcessing ? <RefreshCw className="h-3 w-3 mr-1 animate-spin" /> : <XCircle className="h-3 w-3 mr-1" />}
                              Reject {selectedProposalIds.size}
                            </Button>
                          </>
                        )}
                        {proposals.length > 0 && (
                          <button className="text-xs text-muted-foreground underline hover:no-underline" onClick={toggleSelectAll}>
                            {selectedProposalIds.size === proposals.length ? 'Deselect all' : `Select all ${proposals.length}`}
                          </button>
                        )}
                        <Button size="sm" variant="ghost" onClick={fetchProposals} disabled={loadingProposals}>
                          <RefreshCw className={`h-3 w-3 ${loadingProposals ? 'animate-spin' : ''}`} />
                        </Button>
                      </div>
                    </div>
                  </CardHeader>
                  <CardContent className="pt-3">
                    {loadingProposals ? (
                      <div className="space-y-2">{[1,2,3].map(i => <Skeleton key={i} className="h-12" />)}</div>
                    ) : proposals.length === 0 ? (
                      <div className="flex items-center gap-2 py-2 text-sm text-green-600">
                        <CheckCircle2 className="h-4 w-4" />
                        Queue is clear — all boundaries auto-approved or reviewed
                      </div>
                    ) : (
                      <div className="space-y-2 max-h-96 overflow-y-auto">
                        <p className="text-xs text-muted-foreground mb-2">
                          {selectedProposalIds.size > 0 ? `${selectedProposalIds.size} of ${proposals.length} selected` : `${proposals.length} pending`}
                        </p>
                        {proposals.map(proposal => (
                          <ProposalRow
                            key={proposal.id}
                            proposal={proposal}
                            selected={selectedProposalIds.has(proposal.id)}
                            onToggle={toggleProposalSelection}
                          />
                        ))}
                      </div>
                    )}
                  </CardContent>
                </Card>
              )
            }

            const passKey = step.key as 'pass1' | 'pass2' | 'pass3' | 'pass4' | 'pass4b' | 'pass5'
            const isRunning = runningPass === passKey

            return (
              <Card key={step.key} className="overflow-hidden">
                <CardHeader className="pb-2">
                  <div className="flex items-center justify-between">
                    <div className="flex items-center gap-2">
                      <div className="h-6 w-6 rounded-full bg-primary/10 flex items-center justify-center text-xs font-bold text-primary">
                        {idx + 1}
                      </div>
                      <div>
                        <CardTitle className="text-sm">{step.label}</CardTitle>
                        <p className="text-xs text-muted-foreground">{step.description}</p>
                      </div>
                    </div>
                    <Button
                      size="sm"
                      onClick={() => runPass(passKey)}
                      disabled={!!runningPass}
                    >
                      {isRunning ? (
                        <>
                          <RefreshCw className="h-3 w-3 mr-1 animate-spin" />
                          Running…
                        </>
                      ) : (
                        <>
                          <Play className="h-3 w-3 mr-1" />
                          Run
                        </>
                      )}
                    </Button>
                  </div>
                </CardHeader>
                {/* Progress bars while running paginated passes */}
                {(['pass3', 'pass4', 'pass4b'] as const).includes(step.key as any) && isRunning && passProgress[step.key] && (
                  <CardContent className="border-t bg-muted/20 py-3">
                    <div className="space-y-1.5">
                      <p className="text-xs text-muted-foreground font-medium">
                        Processing… {passProgress[step.key]!.done} / {passProgress[step.key]!.total || '?'}
                        {step.key === 'pass4b' ? ' files' : ' chunks'}
                      </p>
                      {passProgress[step.key]!.total > 0 && (
                        <div className="w-full bg-muted rounded-full h-1.5">
                          <div
                            className="bg-primary h-1.5 rounded-full transition-all"
                            style={{ width: `${Math.min(100, Math.round((passProgress[step.key]!.done / passProgress[step.key]!.total) * 100))}%` }}
                          />
                        </div>
                      )}
                    </div>
                  </CardContent>
                )}

                {/* Progress bar + cost estimate for Pass 3 (AI Document) */}
                {step.key === 'pass3' && (
                  <CardContent className={`border-t py-3 ${isRunning ? 'bg-yellow-50 dark:bg-yellow-950/20' : 'bg-yellow-50 dark:bg-yellow-950/20'}`}>
                    {isRunning && passProgress['pass3'] ? (
                      <div className="space-y-1.5">
                        <p className="text-xs text-yellow-800 dark:text-yellow-300 font-medium">
                          Documenting… {passProgress['pass3'].done} / {passProgress['pass3'].total || '?'} chunks
                        </p>
                        {passProgress['pass3'].total > 0 && (
                          <div className="w-full bg-yellow-200 dark:bg-yellow-900 rounded-full h-1.5">
                            <div
                              className="bg-yellow-500 h-1.5 rounded-full transition-all"
                              style={{ width: `${Math.min(100, Math.round((passProgress['pass3'].done / passProgress['pass3'].total) * 100))}%` }}
                            />
                          </div>
                        )}
                      </div>
                    ) : (
                      <div className="space-y-0.5 text-xs">
                        {loadingEstimate ? (
                          <p className="text-xs text-muted-foreground">Calculating cost estimate…</p>
                        ) : costEstimate ? (
                          <div className="flex items-start gap-2">
                            <DollarSign className="h-4 w-4 text-yellow-600 shrink-0 mt-0.5" />
                            <div className="space-y-0.5 text-xs">
                              <p className="font-medium text-yellow-800 dark:text-yellow-300">
                                Estimated cost: ${costEstimate.estimated_cost_usd ?? '?'} ({costEstimate.model ?? 'gpt-4o-mini'})
                              </p>
                              <p className="text-muted-foreground">
                                {costEstimate.chunks_to_document ?? 0} chunks to document
                                · {costEstimate.already_documented ?? 0} already done
                                · ~{((costEstimate.estimated_total_tokens ?? 0) / 1000).toFixed(0)}K tokens
                              </p>
                              <button
                                className="text-yellow-700 dark:text-yellow-400 underline hover:no-underline"
                                onClick={fetchCostEstimate}
                              >
                                Refresh estimate
                              </button>
                            </div>
                          </div>
                        ) : (
                          <p className="text-xs text-muted-foreground">
                            Run Pass 2 first to generate chunks before estimating cost.
                          </p>
                        )}
                      </div>
                    )}
                  </CardContent>
                )}

                {/* Cost estimate banner for Pass 4 (Embed Chunks) */}
                {step.key === 'pass4' && !isRunning && (
                  <CardContent className="border-t bg-blue-50 dark:bg-blue-950/20 py-3">
                    {loadingEmbeddingEstimate ? (
                      <p className="text-xs text-muted-foreground">Calculating embedding cost…</p>
                    ) : embeddingEstimate ? (
                      <div className="flex items-start gap-2">
                        <DollarSign className="h-4 w-4 text-blue-600 shrink-0 mt-0.5" />
                        <div className="space-y-0.5 text-xs">
                          <p className="font-medium text-blue-800 dark:text-blue-300">
                            Estimated cost: ${embeddingEstimate.estimated_cost_usd ?? '?'} ({embeddingEstimate.model ?? 'text-embedding-3-small'})
                          </p>
                          <p className="text-muted-foreground">
                            {embeddingEstimate.chunks_to_embed ?? 0} chunks × {embeddingEstimate.vectors_per_chunk ?? 3} vectors
                            · {embeddingEstimate.already_embedded ?? 0} already embedded
                            · ~{((embeddingEstimate.estimated_tokens ?? 0) / 1000).toFixed(0)}K tokens
                          </p>
                          <button
                            className="text-blue-700 dark:text-blue-400 underline hover:no-underline"
                            onClick={fetchEmbeddingEstimate}
                          >
                            Refresh estimate
                          </button>
                        </div>
                      </div>
                    ) : (
                      <p className="text-xs text-muted-foreground">
                        Run Pass 2 first to generate chunks before estimating cost.
                      </p>
                    )}
                  </CardContent>
                )}
                {passResults[passKey] && !isRunning && (
                  <CardContent className="border-t bg-muted/30">
                    <pre className="text-xs font-mono overflow-auto max-h-40 whitespace-pre-wrap">
                      {JSON.stringify(passResults[passKey], null, 2)}
                    </pre>
                  </CardContent>
                )}
              </Card>
            )
          })}


      </div>
    </div>
  )
}
