import { useCallback, useEffect, useState } from 'react'
import { ChevronDown, ChevronRight, Clock, Cpu, ScrollText, ShieldAlert, Wrench } from 'lucide-react'
import { toast } from 'sonner'
import { AppShell } from '@/components/AppShell'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Label } from '@/components/ui/label'
import { Skeleton } from '@/components/ui/skeleton'
import { Textarea } from '@/components/ui/textarea'
import { REVIEWS_CHANGED } from '@/components/AppShell'
import { AgentFace } from '@/components/AgentFace'
import { MarkdownEditor } from '@/components/MarkdownEditor'
import { api } from '@/lib/api'
import { diffWords } from '@/lib/diff'
import { useAuth } from '@/store/auth'

interface ReviewItem {
  id: string
  teamId: string
  agentRunId: string
  agentId: string
  workItemId: string
  actionKind: string
  risk: string
  title: string
  content: string
  childTitles: string[]
  trace: string | null
  status: string
  createdAtUtc: string
}

interface RunDetail {
  status: string
  output: string | null
  prompt: string | null
  error: string | null
  trace: string | null
  resultJson: string | null
  latencyMs: number | null
}

interface RunTrace {
  agent?: string
  autonomy?: string
  skills?: string[]
  tools?: string[]
  docs?: string[]
  memories?: number
  product?: { productId?: string | null; identity?: boolean }
  task?: { taskType?: string }
}

interface RunResult {
  action?: string
  risk?: string
  skill?: string
  toolCalls?: { tool: string; server?: string | null; ok: boolean }[]
}

function parseJson<T>(value: string | null | undefined): T | null {
  if (!value) return null
  try {
    return JSON.parse(value) as T
  } catch {
    return null
  }
}

export function ReviewsPage() {
  const organizationId = useAuth((s) => s.organizationId)
  const [items, setItems] = useState<ReviewItem[] | null>(null)

  const load = useCallback(async () => {
    if (!organizationId) return
    try {
      setItems(await api.get<ReviewItem[]>(`/api/governance/reviews?organizationId=${organizationId}`))
    } catch (err) {
      toast.error((err as Error).message)
      setItems([])
    }
  }, [organizationId])

  useEffect(() => {
    void load()
  }, [load])

  return (
    <AppShell>
      <div className="mx-auto max-w-3xl p-6">
        <div className="mb-6">
          <h1 className="text-2xl font-semibold tracking-tight">Review inbox</h1>
          <p className="text-sm text-muted-foreground">
            Held agent actions awaiting your decision. See the action, the result, and the run log before
            you approve. Your edits feed the metric.
          </p>
        </div>

        {items === null && (
          <div className="flex flex-col gap-4">
            <Skeleton className="h-40 w-full" />
            <Skeleton className="h-40 w-full" />
          </div>
        )}

        {items?.length === 0 && (
          <Card>
            <CardContent className="py-10 text-center text-sm text-muted-foreground">
              Nothing is waiting on you. Held agent actions will appear here.
            </CardContent>
          </Card>
        )}

        <div className="flex flex-col gap-4">
          {items?.map((item) => (
            <ReviewCard
              key={item.id}
              item={item}
              onDecided={(id) => setItems((s) => s?.filter((x) => x.id !== id) ?? s)}
            />
          ))}
        </div>
      </div>
    </AppShell>
  )
}

function ReviewCard({ item, onDecided }: { item: ReviewItem; onDecided: (id: string) => void }) {
  const [content, setContent] = useState(item.content)
  const [childrenText, setChildrenText] = useState(item.childTitles.join('\n'))
  const [showLog, setShowLog] = useState(false)
  const [run, setRun] = useState<RunDetail | null>(null)
  const [busy, setBusy] = useState(false)

  const destructive = item.risk.toLowerCase() === 'destructive'
  const childCount = childrenText.split('\n').map((l) => l.trim()).filter(Boolean).length

  // Lazily pull the full run (latency, tool calls, raw output, assembled prompt) the first time the
  // approver opens the log.
  const toggleLog = async () => {
    const next = !showLog
    setShowLog(next)
    if (next && !run) {
      try {
        setRun(await api.get<RunDetail>(`/api/assembler/runs/${item.agentRunId}`))
      } catch {
        // The run row may be gone; the assembly trace on the item still renders below.
      }
    }
  }

  async function decide(action: 'approve' | 'sendback') {
    setBusy(true)
    try {
      if (action === 'approve') {
        const childTitles = childrenText.split('\n').map((line) => line.trim()).filter(Boolean)
        const result = await api.post<{ editDistance: number | null; decision: string }>(
          `/api/governance/reviews/${item.id}/approve`,
          { content, childTitles },
        )
        const distance = result.editDistance ?? 0
        toast.success(
          result.decision === 'EditedAndApproved'
            ? `Approved with edits — edit distance ${distance.toFixed(3)}`
            : 'Approved as proposed',
        )
      } else {
        await api.post(`/api/governance/reviews/${item.id}/sendback`, {})
        toast.info('Sent back to the agent')
      }
      onDecided(item.id)
      window.dispatchEvent(new Event(REVIEWS_CHANGED))
    } catch (err) {
      toast.error((err as Error).message)
    } finally {
      setBusy(false)
    }
  }

  return (
    <Card>
      <CardHeader>
        <div className="flex items-center gap-3">
          <AgentFace size="md" monogram={item.agentId} state="review" className="shrink-0" />
          <div className="min-w-0 flex-1">
            <CardTitle className="truncate text-base">{item.title}</CardTitle>
            <div className="mt-1 flex items-center gap-2">
              <Badge variant="secondary">{item.actionKind}</Badge>
              <Badge variant={destructive ? 'destructive' : 'outline'}>
                {destructive && <ShieldAlert />}
                {item.risk}
              </Badge>
              <span className="text-xs text-muted-foreground">{new Date(item.createdAtUtc).toLocaleString()}</span>
            </div>
          </div>
        </div>
      </CardHeader>
      <CardContent className="flex flex-col gap-4">
        {/* Action — what approving will actually do. */}
        <div
          className={`rounded-lg border px-3 py-2 text-sm ${
            destructive ? 'border-destructive/40 bg-destructive/10 text-destructive' : 'border-primary/30 bg-primary/5'
          }`}
        >
          <span className="font-medium capitalize">{item.actionKind.replace(/-/g, ' ')}</span> ·{' '}
          {destructive
            ? 'Destructive — always held for a human.'
            : `On approve, this artifact is written to the board${childCount ? ` and ${childCount} child task${childCount === 1 ? '' : 's'} created` : ''}.`}
        </div>

        {/* Result — the proposed artifact + child tasks (both editable). */}
        <div className="flex flex-col gap-2">
          <Label htmlFor={`content-${item.id}`}>Result · proposed artifact</Label>
          <MarkdownEditor id={`content-${item.id}`} value={content} onChange={setContent} rows={6} mono />
        </div>

        {content !== item.content && (
          <div className="flex flex-col gap-2">
            <Label>Your edits (vs the proposal)</Label>
            <div className="max-h-40 overflow-auto whitespace-pre-wrap rounded-lg border bg-muted/40 p-3 text-xs leading-relaxed">
              {diffWords(item.content, content).map((segment, i) =>
                segment.kind === 'same' ? (
                  <span key={i}>{segment.text}</span>
                ) : segment.kind === 'removed' ? (
                  <del key={i} className="rounded bg-destructive/15 text-destructive">{segment.text}</del>
                ) : (
                  <ins key={i} className="rounded bg-seat-ai/15 font-medium text-primary no-underline">{segment.text}</ins>
                ),
              )}
            </div>
          </div>
        )}

        <div className="flex flex-col gap-2">
          <Label htmlFor={`children-${item.id}`}>Child tasks (one per line)</Label>
          <Textarea
            id={`children-${item.id}`}
            value={childrenText}
            onChange={(e) => setChildrenText(e.target.value)}
            rows={4}
            placeholder="No child tasks proposed — add lines to create them on approval."
          />
        </div>

        {/* Run log — how the agent got here. */}
        <button
          type="button"
          onClick={toggleLog}
          className="flex items-center gap-1.5 self-start text-xs font-medium text-primary hover:underline"
        >
          {showLog ? <ChevronDown className="size-3.5" /> : <ChevronRight className="size-3.5" />}
          <ScrollText className="size-3.5" /> Run log
        </button>
        {showLog && <RunLog item={item} run={run} />}

        <div className="flex items-center justify-end gap-2">
          <Button variant="outline" disabled={busy} onClick={() => decide('sendback')}>Send back</Button>
          <Button disabled={busy} onClick={() => decide('approve')}>{busy ? 'Working…' : 'Approve'}</Button>
        </div>
      </CardContent>
    </Card>
  )
}

function RunLog({ item, run }: { item: ReviewItem; run: RunDetail | null }) {
  // The assembly trace is on the review item; the run adds latency, tool-call outcomes, and raw output.
  const trace = parseJson<RunTrace>(run?.trace ?? item.trace)
  const result = parseJson<RunResult>(run?.resultJson)
  const [showRaw, setShowRaw] = useState(false)
  const [showPrompt, setShowPrompt] = useState(false)
  const toolCalls = result?.toolCalls ?? []

  return (
    <div className="flex flex-col gap-2 rounded-lg border bg-muted/30 p-3 text-xs">
      <div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-muted-foreground">
        <span className="inline-flex items-center gap-1">
          <Cpu className="size-3.5" /> {trace?.agent ?? 'agent'} · {trace?.autonomy ?? '—'}
        </span>
        {run?.latencyMs != null && (
          <span className="inline-flex items-center gap-1">
            <Clock className="size-3.5" /> {(run.latencyMs / 1000).toFixed(1)}s
          </span>
        )}
        {trace?.product?.identity && <span>· product identity included</span>}
        {typeof trace?.memories === 'number' && <span>· {trace.memories} memory hit{trace.memories === 1 ? '' : 's'}</span>}
      </div>

      <LogRow label="Skills applied" value={trace?.skills?.length ? trace.skills.join(', ') : 'none'} />
      {trace?.tools?.length ? <LogRow label="Tools available" value={trace.tools.join(', ')} /> : null}
      {trace?.docs?.length ? <LogRow label="Docs" value={trace.docs.join(', ')} /> : null}

      <div className="flex gap-2">
        <span className="w-28 shrink-0 font-medium text-foreground">Tools called</span>
        <span className="min-w-0 flex-1">
          {toolCalls.length === 0 ? (
            <span className="text-muted-foreground">none</span>
          ) : (
            <span className="flex flex-col gap-0.5">
              {toolCalls.map((t, i) => (
                <span key={i} className="inline-flex items-center gap-1">
                  <Wrench className="size-3" /> {t.tool}
                  {t.server ? ` · ${t.server}` : ''}
                  <Badge variant={t.ok ? 'outline' : 'destructive'} className="ml-1 h-4 px-1.5 text-[10px]">
                    {t.ok ? 'ok' : 'failed'}
                  </Badge>
                </span>
              ))}
            </span>
          )}
        </span>
      </div>

      {run?.error && <LogRow label="Error" value={run.error} />}

      {run?.output && (
        <div className="flex flex-col gap-1">
          <button type="button" onClick={() => setShowRaw((v) => !v)} className="flex items-center gap-1 self-start font-medium text-foreground hover:underline">
            {showRaw ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />} Raw model output
          </button>
          {showRaw && <pre className="max-h-48 overflow-auto rounded bg-background/60 p-2 whitespace-pre-wrap">{run.output}</pre>}
        </div>
      )}

      {run?.prompt && (
        <div className="flex flex-col gap-1">
          <button type="button" onClick={() => setShowPrompt((v) => !v)} className="flex items-center gap-1 self-start font-medium text-foreground hover:underline">
            {showPrompt ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />} Assembled prompt
          </button>
          {showPrompt && <pre className="max-h-56 overflow-auto rounded bg-background/60 p-2 whitespace-pre-wrap">{run.prompt}</pre>}
        </div>
      )}

      {!run && !trace && <span className="text-muted-foreground">Loading run…</span>}
    </div>
  )
}

function LogRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex gap-2">
      <span className="w-28 shrink-0 font-medium text-foreground">{label}</span>
      <span className="min-w-0 flex-1 break-words">{value}</span>
    </div>
  )
}
