import { useCallback } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { TerminalSquare, Trash2, RefreshCw } from 'lucide-react';
import { useClient } from 'hooks/useClient';

// Every shell the pty sidecar is holding, whether or not a panel is pointing at it.
//
// A terminal panel keeps its session id across unmounts so reopening re-attaches, and now kills the shell
// when the panel is genuinely closed (`usePanelClose`). This is the backstop for the rest: shells orphaned
// before that landed, ones whose kill request failed, and anything started outside a panel.
//
// `/_officer/…` is the sidecar's own prefix for the routes it exposes to the platform rather than to a
// terminal. The proxy strips `/api/terminal` and forwards the remainder verbatim, so leaving it out — as
// this file did — reaches the sidecar as `/sessions`, which it answers 404 to. That is a panel that always
// read "No shells running" and a kill button that quietly did nothing.

type PtySession = {
  sessionId: string;
  cols: number;
  rows: number;
  createdAt: number;
  lastActivityAt: number;
  title?: string;
  pid?: number;
  // How many sockets are attached. The sidecar has always sent it and this list has always dropped it,
  // which left every row looking the same — yet it is the only field that says whether a shell is one
  // you are looking at or one nothing is pointing at any more.
  clients?: number;
};

const RELATIVE_UNITS: [limit: number, div: number, unit: string][] = [
  [60_000, 1000, 's'],
  [3_600_000, 60_000, 'm'],
  [86_400_000, 3_600_000, 'h'],
];

const since = (ts: number): string => {
  if (!ts) return '—';
  const delta = Date.now() - ts;
  for (const [limit, div, unit] of RELATIVE_UNITS) {
    if (delta < limit) return `${Math.max(0, Math.floor(delta / div))}${unit}`;
  }
  return `${Math.floor(delta / 86_400_000)}d`;
};

export const RunningShells = () => {
  const client = useClient();
  const queryClient = useQueryClient();

  const { data, isLoading, refetch } = useQuery({
    queryKey: ['terminal-sessions'],
    queryFn: () => client.get<{ sessions: PtySession[] }>('/terminal/_officer/sessions'),
    refetchInterval: 5000,
  });

  const kill = useCallback(
    async (sessionId: string) => {
      await client.delete(`/terminal/_officer/sessions/${sessionId}`);
      // The shell dies asynchronously (the sidecar answers on its pty:exit broadcast, not to us), so give
      // it a beat before asking again rather than showing a row that is already gone.
      setTimeout(() => queryClient.invalidateQueries({ queryKey: ['terminal-sessions'] }), 300);
    },
    [client, queryClient],
  );

  const sessions = data?.sessions ?? [];

  return (
    <div className="flex h-full w-full flex-col overflow-hidden text-sm">
      <div className="flex shrink-0 items-center gap-2 border-b border-white/10 px-3 py-2">
        <TerminalSquare className="h-3.5 w-3.5 shrink-0" />
        <span className="text-xs font-medium">Running shells</span>
        <span className="text-[10px] opacity-60">{sessions.length}</span>
        <button
          type="button"
          onClick={() => refetch()}
          title="Refresh"
          className="ml-auto cursor-pointer opacity-60 hover:opacity-100"
        >
          <RefreshCw className="h-3.5 w-3.5" />
        </button>
      </div>

      <div className="flex-1 overflow-y-auto">
        {isLoading && <p className="px-3 py-3 text-xs opacity-60">Loading…</p>}
        {!isLoading && sessions.length === 0 && (
          <p className="px-3 py-3 text-xs opacity-60">No shells running.</p>
        )}
        {sessions.map((s) => (
          <div key={s.sessionId} className="flex items-center gap-2 border-b border-white/5 px-3 py-2">
            <div className="min-w-0 flex-1">
              {/* The title the shell set for itself (OSC 0/2) — usually the running command. It is what
                  makes an abandoned shell identifiable instead of a bare uuid. */}
              <p className="truncate text-xs font-medium">
                {s.title || 'shell'}
                {s.clients === 0 && (
                  <span className="ml-1.5 rounded bg-amber-500/15 px-1 py-px text-[10px] text-amber-400">
                    orphan
                  </span>
                )}
              </p>
              <p className="truncate font-mono text-[10px] opacity-50">
                {s.sessionId.slice(0, 8)} · {s.cols}×{s.rows}
                {s.pid ? ` · pid ${s.pid}` : ''}
                {s.clients === undefined ? '' : ` · ${s.clients} attached`} · idle {since(s.lastActivityAt)} · up{' '}
                {since(s.createdAt)}
              </p>
            </div>
            <button
              type="button"
              onClick={() => kill(s.sessionId)}
              title="Kill this shell"
              className="shrink-0 cursor-pointer p-1 opacity-60 hover:text-red-400 hover:opacity-100"
            >
              <Trash2 className="h-3.5 w-3.5" />
            </button>
          </div>
        ))}
      </div>
    </div>
  );
};
