import { useCallback, useMemo, useState } from 'react';

// Normalized external-agent status (#10). Consumed from the
// `agent_status_updated` WS frame the coder backend publishes:
//   { type: 'agent_status_updated'; chat_id; agent; status; reason?; at }
// BooCoder collapses ~30 vendor lifecycle events into these four buckets:
//   working  — turn in flight
//   blocked  — waiting on a permission / approval
//   idle     — clean completion
//   error    — crash / failure
export type AgentStatus = 'working' | 'blocked' | 'idle' | 'error';

export interface AgentStatusEntry {
  status: AgentStatus;
  reason?: string;
  at: string;
}

const key = (chatId: string, agent: string): string => `${chatId}:${agent}`;

// Per-(chat,agent) live status map. The dot reflects the latest frame for the
// active agent in the current chat; entries are reset when the chat switches so
// a stale "working"/"blocked" from a previous chat never leaks into the next.
export function useAgentStatus() {
  const [map, setMap] = useState<Record<string, AgentStatusEntry>>({});

  const record = useCallback(
    (chatId: string, agent: string, entry: AgentStatusEntry) => {
      setMap((prev) => ({ ...prev, [key(chatId, agent)]: entry }));
    },
    [],
  );

  // Drop every entry for a chat (called on chat switch). No-op when nothing
  // matches so it's safe to call unconditionally from an effect.
  const reset = useCallback((chatId: string | undefined) => {
    setMap((prev) => {
      if (!chatId) return prev;
      const prefix = `${chatId}:`;
      let changed = false;
      const next: Record<string, AgentStatusEntry> = {};
      for (const [k, v] of Object.entries(prev)) {
        if (k.startsWith(prefix)) {
          changed = true;
          continue;
        }
        next[k] = v;
      }
      return changed ? next : prev;
    });
  }, []);

  const get = useCallback(
    (chatId: string | undefined, agent: string | undefined): AgentStatusEntry | null => {
      if (!chatId || !agent) return null;
      return map[key(chatId, agent)] ?? null;
    },
    [map],
  );

  return useMemo(() => ({ record, reset, get }), [record, reset, get]);
}
