import { useEffect, useRef, useState } from 'react';
import { Link, useParams } from 'react-router';
import { useClient } from 'hooks/useClient';
import { Radio, FileText, Activity as ActivityIcon } from 'lucide-react';

type TaskRow = { id: string; source: 'harness'; cwd: string; sizeBytes: number; updatedAt: number; active: boolean };
type DetachedRow = { id: string; source: 'detached'; path: string; announcedAt: number };
type Registry = { tasks: TaskRow[]; detached: DetachedRow[] };
type ProgressLine = { phase?: string; status?: string; pct?: number; detail?: string; cap?: string; job?: string };

const POLL_MS = 3000;
const MAX_LINES = 600;

// Which run you are following is `/activity/:id`. The two row kinds stream through different query
// params (`task=` for a harness file, `path=` for a detached job), so the URL carries the id only and
// the screen re-derives the param from the registry row — one address shape for both kinds.
export const ActivityScreen = () => {
  const { token, get } = useClient();
  const [reg, setReg] = useState<Registry>({ tasks: [], detached: [] });
  const [regLoaded, setRegLoaded] = useState(false);
  const selectedId = useParams<{ id: string }>().id ?? null;
  const [lines, setLines] = useState<string[]>([]);
  const [progress, setProgress] = useState<ProgressLine | null>(null);
  const esRef = useRef<EventSource | null>(null);
  const scrollRef = useRef<HTMLDivElement | null>(null);

  // Poll the registry (harness task files + announced detached jobs).
  useEffect(() => {
    let alive = true;
    const tick = () =>
      get<Registry>('/activity/tasks')
        .then((r) => {
          if (alive) {
            setReg(r);
            setRegLoaded(true);
          }
        })
        .catch(() => {});
    tick();
    const iv = setInterval(tick, POLL_MS);
    return () => {
      alive = false;
      clearInterval(iv);
    };
  }, []);

  // The row backing the open id, and the stream query it implies. A string rather than the row object,
  // so the 3s registry poll — which replaces every row — does not tear down and re-open the stream.
  const row = selectedId
    ? (reg.tasks.find((t) => t.id === selectedId) ?? reg.detached.find((d) => d.id === selectedId))
    : undefined;
  const query = !row
    ? null
    : row.source === 'harness'
      ? `task=${encodeURIComponent(row.id)}`
      : `path=${encodeURIComponent(row.path)}`;

  // Live-tail the selected task via SSE (EventSource can't set headers → token in the query string).
  useEffect(() => {
    esRef.current?.close();
    setLines([]);
    setProgress(null);
    if (!query) return;
    const es = new EventSource(`/api/activity/stream?${query}&token=${encodeURIComponent(token ?? '')}`);
    esRef.current = es;
    es.onmessage = (ev) => {
      try {
        const d = JSON.parse(ev.data) as { kind: string; text?: string; progress?: ProgressLine };
        if (d.kind === 'progress' && d.progress) setProgress(d.progress);
        else if (d.kind === 'line' && typeof d.text === 'string')
          setLines((prev) => [...prev.slice(-(MAX_LINES - 1)), d.text!]);
      } catch {
        /* ignore */
      }
    };
    return () => es.close();
  }, [query, token]);

  useEffect(() => {
    scrollRef.current?.scrollTo(0, scrollRef.current.scrollHeight);
  }, [lines]);

  const rowCls = (active: boolean) =>
    `flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm ${active ? 'bg-muted text-foreground' : 'text-muted-foreground hover:bg-muted/60 hover:text-foreground'}`;

  return (
    <div className="flex h-full w-full">
      <aside className="flex w-72 shrink-0 flex-col overflow-y-auto border-r border-border p-3">
        <div className="mb-3 flex items-center gap-2 text-sm font-semibold text-foreground">
          <ActivityIcon size={16} className="text-primary" /> Activity
        </div>

        <div className="mb-1 px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
          Background tasks
        </div>
        {reg.tasks.length === 0 && <p className="px-2 py-1 text-xs text-muted-foreground">none running</p>}
        {reg.tasks.map((t) => (
          <Link
            key={t.id}
            to={`/activity/${encodeURIComponent(t.id)}`}
            className={rowCls(selectedId === t.id)}
            title={t.cwd}
          >
            <span
              className={`inline-block h-2 w-2 shrink-0 rounded-full ${t.active ? 'bg-emerald-500 animate-pulse' : 'bg-muted-foreground/40'}`}
            />
            <span className="truncate font-mono text-xs">{t.id}</span>
          </Link>
        ))}

        {reg.detached.length > 0 && (
          <div className="mb-1 mt-4 px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
            Detached
          </div>
        )}
        {reg.detached.map((d) => (
          <Link
            key={d.id}
            to={`/activity/${encodeURIComponent(d.id)}`}
            className={rowCls(selectedId === d.id)}
            title={d.path}
          >
            <FileText size={13} className="shrink-0" />
            <span className="truncate">{d.id}</span>
          </Link>
        ))}
      </aside>

      <main className="flex min-w-0 flex-1 flex-col">
        {selectedId ? (
          <>
            <div className="shrink-0 border-b border-border p-3">
              <div className="flex items-center gap-2 text-sm text-foreground">
                <Radio size={14} className="text-primary" />
                <span className="truncate font-mono">{selectedId}</span>
              </div>
              {progress && (
                <div className="mt-2">
                  <div className="flex items-center justify-between text-xs text-muted-foreground">
                    <span className="truncate">
                      {[progress.cap, progress.phase].filter(Boolean).join(' · ')}
                      {progress.status ? ` (${progress.status})` : ''}
                    </span>
                    <span className="shrink-0 pl-2">
                      {progress.detail ?? (typeof progress.pct === 'number' ? `${progress.pct}%` : '')}
                    </span>
                  </div>
                  {typeof progress.pct === 'number' && (
                    <div className="mt-1 h-1.5 w-full overflow-hidden rounded-full bg-muted">
                      <div
                        className="h-full rounded-full bg-primary transition-all"
                        style={{ width: `${Math.min(100, Math.max(0, progress.pct))}%` }}
                      />
                    </div>
                  )}
                </div>
              )}
            </div>
            <div
              ref={scrollRef}
              className="min-h-0 flex-1 overflow-y-auto bg-black/30 p-3 font-mono text-xs text-foreground/80"
            >
              {lines.length === 0 ? (
                <span className="text-muted-foreground">
                  {query ? (
                    'waiting for output…'
                  ) : regLoaded ? (
                    <>
                      no run called <span className="font-mono">{selectedId}</span> is in the registry — it finished, or
                      it never started.{' '}
                      <Link to="/activity" className="underline">
                        Back to the list
                      </Link>
                    </>
                  ) : (
                    'loading…'
                  )}
                </span>
              ) : (
                lines.map((l, i) => (
                  <div key={i} className="whitespace-pre-wrap break-words">
                    {l}
                  </div>
                ))
              )}
            </div>
          </>
        ) : (
          <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
            Select a task to follow its live output
          </div>
        )}
      </main>
    </div>
  );
};
