import { useEffect, useMemo, useState } from 'react';
import { ArrowLeft, Beaker, CheckCircle2, FileText, ScrollText, Swords, XCircle } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { api } from '@/api/client';
import type { BattleShape, FlowRunRow } from '@/api/types';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { useSidebar } from '@/hooks/useSidebar';
import { cn } from '@/lib/utils';

function useFetch<T>(fetcher: () => Promise<T>): {
  data: T | null;
  loading: boolean;
  error: string | null;
  retry: () => void;
} {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  function load() {
    setLoading(true);
    setError(null);
    fetcher()
      .then(setData)
      .catch((err: unknown) => {
        setError(err instanceof Error ? err.message : 'failed to load data');
      })
      .finally(() => setLoading(false));
  }

  useEffect(() => { load(); }, []); // eslint-disable-line react-hooks/exhaustive-deps

  return { data, loading, error, retry: load };
}

function SkeletonBar({ className }: { className?: string }) {
  return <div className={cn('animate-pulse rounded bg-muted/40', className)} />;
}

function formatDate(iso: string | null | undefined): string {
  if (!iso) return '—';
  return new Date(iso).toLocaleDateString(undefined, {
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  });
}

function formatDuration(startIso: string, endIso?: string | null): string {
  const start = new Date(startIso).getTime();
  const end = endIso ? new Date(endIso).getTime() : Date.now();
  const ms = end - start;
  if (ms < 0) return '—';
  const s = Math.round(ms / 1000);
  if (s < 60) return `${s}s`;
  if (s < 3600) return `${Math.floor(s / 60)}m${String(s % 60).padStart(2, '0')}s`;
  return `${Math.floor(s / 3600)}h${String(Math.floor((s % 3600) / 60)).padStart(2, '0')}m`;
}

function truncate(str: string, max: number): string {
  if (str.length <= max) return str;
  return str.slice(0, max) + '…';
}

type DotStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'pending';

function StatusDot({ status }: { status: DotStatus }) {
  if (status === 'running') {
    return (
      <span
        aria-label="running"
        className="inline-block w-2.5 h-2.5 rounded-full border-2 border-emerald-500 border-t-transparent animate-spin shrink-0"
      />
    );
  }
  const cls =
    status === 'completed'
      ? 'bg-emerald-500'
      : status === 'failed'
        ? 'bg-destructive'
        : status === 'cancelled'
          ? 'bg-muted-foreground/20'
          : 'bg-muted-foreground/40'; // pending
  return <span aria-label={status} className={cn('inline-block w-2 h-2 rounded-full shrink-0', cls)} />;
}

type TabId = 'runs' | 'battles';

function TabBar({ active, onChange }: { active: TabId; onChange: (t: TabId) => void }) {
  return (
    <div className="flex gap-1 border-b pb-px">
      {[
        { id: 'runs' as TabId, label: 'Analysis Runs', icon: FileText },
        { id: 'battles' as TabId, label: 'Arena Battles', icon: Swords },
      ].map((tab) => (
        <button
          key={tab.id}
          type="button"
          onClick={() => onChange(tab.id)}
          className={cn(
            'flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-t-md border border-b-0 -mb-px transition-colors',
            active === tab.id
              ? 'bg-background border-border text-foreground'
              : 'border-transparent text-muted-foreground hover:text-foreground hover:bg-muted/30',
          )}
        >
          <tab.icon className="size-3.5" />
          <span>{tab.label}</span>
        </button>
      ))}
    </div>
  );
}

function EmptyState({ message }: { message: string }) {
  return <p className="text-sm text-muted-foreground py-8 text-center">{message}</p>;
}

function ProjectSelector({
  projects,
  value,
  onChange,
}: {
  projects: Array<{ id: string; name: string }>;
  value: string;
  onChange: (id: string) => void;
}) {
  return (
    <select
      value={value}
      onChange={(e) => onChange(e.target.value)}
      className="text-sm bg-muted/30 border border-border rounded px-2 py-1 text-foreground"
    >
      {projects.map((p) => (
        <option key={p.id} value={p.id}>
          {p.name}
        </option>
      ))}
    </select>
  );
}

function AnalysisRunsTab({ projectId }: { projectId: string }) {
  const { data, loading, error, retry } = useFetch(() => api.runs.list(projectId).then((r) => r.runs));

  const [selectedRun, setSelectedRun] = useState<FlowRunRow | null>(null);

  if (loading) {
    return (
      <div className="space-y-2 pt-4">
        {[0, 1, 2, 3].map((i) => (
          <SkeletonBar key={i} className="h-12 w-full" />
        ))}
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex items-center gap-3 text-sm pt-4">
        <span className="text-destructive">{error}</span>
        <Button size="sm" variant="outline" onClick={retry}>
          Retry
        </Button>
      </div>
    );
  }

  if (!data || data.length === 0) {
    return <EmptyState message="No analysis runs yet. Start one from the Workflow button in any chat." />;
  }

  return (
    <div className="pt-4 space-y-2">
      {data.map((run) => (
        <div key={run.id}>
          <button
            type="button"
            onClick={() => setSelectedRun(selectedRun?.id === run.id ? null : run)}
            className={cn(
              'w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm text-left transition-colors hover:bg-muted/30',
              selectedRun?.id === run.id && 'bg-muted/40',
            )}
          >
            <StatusDot status={run.status as DotStatus} />
            <span className="font-medium min-w-0 flex-1 truncate">
              {run.flow_name}
              <span className="text-muted-foreground font-normal ml-1.5 text-xs uppercase">
                {run.band}
              </span>
            </span>
            <span className="text-xs text-muted-foreground tabular-nums hidden sm:block">
              {run.model ? run.model.split('/').pop() : '—'}
            </span>
            <span className="text-xs text-muted-foreground tabular-nums whitespace-nowrap">
              {formatDuration(run.created_at, run.updated_at)}
            </span>
            <span className="text-xs text-muted-foreground whitespace-nowrap">
              {formatDate(run.created_at)}
            </span>
            {run.error && (
              <span className="text-destructive" title={run.error}>
                <XCircle className="size-3.5" />
              </span>
            )}
            {run.status === 'completed' && run.report && (
              <FileText className="size-3.5 text-muted-foreground shrink-0" />
            )}
          </button>

          {/* Expanded detail — report preview */}
          {selectedRun?.id === run.id && run.status === 'completed' && run.report && (
            <div className="ml-8 mr-2 mb-2 p-3 rounded-md bg-muted/20 border border-border/50 text-xs leading-relaxed max-h-60 overflow-y-auto whitespace-pre-wrap font-mono">
              {truncate(run.report, 3000)}
            </div>
          )}
        </div>
      ))}
    </div>
  );
}

function ArenaBattlesTab({ projectId }: { projectId: string }) {
  const { data, loading, error, retry } = useFetch(() => api.battles.list(projectId).then((r) => r.battles));

  const [selectedBattle, setSelectedBattle] = useState<BattleShape | null>(null);

  if (loading) {
    return (
      <div className="space-y-2 pt-4">
        {[0, 1, 2, 3].map((i) => (
          <SkeletonBar key={i} className="h-12 w-full" />
        ))}
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex items-center gap-3 text-sm pt-4">
        <span className="text-destructive">{error}</span>
        <Button size="sm" variant="outline" onClick={retry}>
          Retry
        </Button>
      </div>
    );
  }

  if (!data || data.length === 0) {
    return <EmptyState message="No arena battles yet. Start one from the Arena button in any chat." />;
  }

  return (
    <div className="pt-4 space-y-2">
      {data.map((battle) => {
        const hasAnalysis = battle.status === 'completed' && battle.results_path;
        return (
          <div key={battle.id}>
            <button
              type="button"
              onClick={() => setSelectedBattle(selectedBattle?.id === battle.id ? null : battle)}
              className={cn(
                'w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm text-left transition-colors hover:bg-muted/30',
                selectedBattle?.id === battle.id && 'bg-muted/40',
              )}
            >
              <StatusDot status={
                battle.status === 'completed' ? 'completed'
                : battle.status === 'failed' ? 'failed'
                : battle.status === 'cancelled' ? 'cancelled'
                : 'running'
              } />
              <span className="font-medium min-w-0 flex-1 truncate">
                {battle.battle_type === 'coding' ? 'Coding Battle' : 'Q&A Battle'}
                <span className="text-muted-foreground font-normal ml-1.5 text-xs">
                  {truncate(battle.prompt, 60)}
                </span>
              </span>
              {battle.winner_contestant_id && (
                <span className="text-xs text-emerald-600 dark:text-emerald-400 flex items-center gap-1">
                  <CheckCircle2 className="size-3" />
                  Winner
                </span>
              )}
              {battle.error && (
                <span className="text-destructive" title={battle.error}>
                  <XCircle className="size-3.5" />
                </span>
              )}
              <span className="text-xs text-muted-foreground whitespace-nowrap hidden sm:block">
                {formatDate(battle.created_at)}
              </span>
              {hasAnalysis && (
                <Beaker className="size-3.5 text-muted-foreground shrink-0" />
              )}
            </button>

            {/* Expanded detail — analysis preview */}
            {selectedBattle?.id === battle.id && hasAnalysis && (
              <div className="ml-8 mr-2 mb-2">
                <AnalysisPreview battleId={battle.id} />
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

function AnalysisPreview({ battleId }: { battleId: string }) {
  const { data, loading, error, retry } = useFetch(() => api.battles.getAnalysis(battleId).then((r) => r.text));

  if (loading) {
    return (
      <div className="space-y-2 p-3 rounded-md bg-muted/20 border border-border/50">
        <SkeletonBar className="h-3 w-full" />
        <SkeletonBar className="h-3 w-3/4" />
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex items-center gap-3 p-3 rounded-md bg-muted/20 border border-border/50 text-xs">
        <span className="text-destructive">{error}</span>
        <Button size="sm" variant="outline" onClick={retry}>
          Retry
        </Button>
      </div>
    );
  }

  return (
    <div className="p-3 rounded-md bg-muted/20 border border-border/50 text-xs leading-relaxed max-h-60 overflow-y-auto whitespace-pre-wrap font-mono">
      {data ? truncate(data, 3000) : 'No analysis available.'}
    </div>
  );
}

function SummaryCards({
  runs,
  battles,
}: {
  runs: FlowRunRow[] | null;
  battles: BattleShape[] | null;
}) {
  const totalRuns = runs?.length ?? 0;
  const completedRuns = runs?.filter((r) => r.status === 'completed').length ?? 0;
  const totalBattles = battles?.length ?? 0;
  const completedBattles = battles?.filter((b) => b.status === 'completed').length ?? 0;

  const cards = [
    { label: 'Total Runs', value: totalRuns, icon: FileText, color: 'text-blue-500' },
    { label: 'Completed Runs', value: completedRuns, icon: CheckCircle2, color: 'text-emerald-500' },
    { label: 'Total Battles', value: totalBattles, icon: Swords, color: 'text-violet-500' },
    { label: 'Completed Battles', value: completedBattles, icon: CheckCircle2, color: 'text-emerald-500' },
  ];

  return (
    <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
      {cards.map((c) => (
        <Card key={c.label} size="sm">
          <CardContent className="flex items-start gap-3 pt-3">
            <c.icon className={cn('size-4 shrink-0 mt-0.5', c.color)} />
            <div className="min-w-0">
              <div className="text-lg font-semibold tabular-nums">{c.value}</div>
              <div className="text-xs text-muted-foreground mt-0.5">{c.label}</div>
            </div>
          </CardContent>
        </Card>
      ))}
    </div>
  );
}

function SummaryCardsSkeleton() {
  return (
    <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
      {[0, 1, 2, 3].map((i) => (
        <Card key={i} size="sm">
          <CardContent className="pt-3">
            <SkeletonBar className="h-5 w-16 mb-2" />
            <SkeletonBar className="h-3 w-20" />
          </CardContent>
        </Card>
      ))}
    </div>
  );
}

export function Results() {
  const navigate = useNavigate();
  const { data: sidebar, activeSession } = useSidebar();

  const [tab, setTab] = useState<TabId>('runs');
  const [projectId, setProjectId] = useState<string | null>(null);

  // Derive default project from active session or first project.
  const projects = useMemo(() => {
    return sidebar?.projects?.map((p: { id: string; name: string }) => p) ?? [];
  }, [sidebar]);

  useEffect(() => {
    if (!projectId && projects.length > 0) {
      // Prefer active session's project, else first project.
      const defaultId = activeSession?.project_id ?? projects[0]!.id;
      setProjectId(defaultId);
    }
  }, [projects, activeSession, projectId]);

  function handleBack() {
    if (window.history.length > 1) {
      navigate(-1);
    } else {
      navigate('/');
    }
  }

  const runsFetch = useFetch(
    projectId ? () => api.runs.list(projectId).then((r) => r.runs) : () => Promise.resolve([] as FlowRunRow[]),
  );
  const battlesFetch = useFetch(
    projectId ? () => api.battles.list(projectId).then((r) => r.battles) : () => Promise.resolve([] as BattleShape[]),
  );

  const summaryLoading = runsFetch.loading && battlesFetch.loading;

  return (
    <div className="flex-1 overflow-y-auto">
      <div className="max-w-[1000px] mx-auto w-full px-6 py-6 space-y-6">
        {/* Header */}
        <header className="space-y-2">
          <button
            type="button"
            onClick={handleBack}
            className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground -ml-1 px-1 py-0.5 rounded"
            aria-label="Back"
          >
            <ArrowLeft className="size-4" />
            <span>Back</span>
          </button>
          <div className="flex items-center justify-between gap-4">
            <div>
              <h1 className="text-xl font-semibold flex items-center gap-2">
                <ScrollText className="size-5" />
                Results
              </h1>
              <p className="text-sm text-muted-foreground mt-1">
                Completed orchestrator runs and arena battles.
              </p>
            </div>
            {projects.length > 0 && projectId && (
              <ProjectSelector
                projects={projects}
                value={projectId}
                onChange={setProjectId}
              />
            )}
          </div>
        </header>

        {/* Summary Cards */}
        {summaryLoading ? (
          <SummaryCardsSkeleton />
        ) : (
          <SummaryCards runs={runsFetch.data} battles={battlesFetch.data} />
        )}

        {/* Tab bar */}
        <TabBar active={tab} onChange={setTab} />

        {/* Tab content */}
        {!projectId ? (
          <EmptyState message="Select a project to view results." />
        ) : tab === 'runs' ? (
          <AnalysisRunsTab projectId={projectId} />
        ) : (
          <ArenaBattlesTab projectId={projectId} />
        )}
      </div>
    </div>
  );
}
