import type { SelectedSession } from './apps/ChatHistory';
import { useCallback, useRef } from 'react';
import { defineChannel } from 'hooks/usePanelChannel';

/**
 * Panel channels, declared once each.
 *
 * A channel is a *signal*, not a selection — "something changed, re-read it", "this pane is now showing
 * that". Anything addressable belongs in the URL; see `docs/navigation-audit.md`.
 */

const useFilesRefreshChannel = defineChannel<number>('files:refresh-signal', 0);

// Bumping this used to be done two different ways: `Date.now()` at the Chat sites and
// `setSignal((n) => n + 1)` at the FileViewer ones. The increment is the wrong one — `useGlobal`'s
// functional setter applies against the value captured at render, so two bumps inside one render window
// both compute `snapshot + 1` and the second overwrites the first with the same number. Nobody re-reads,
// and the file that was just written stays stale on screen.
//
// A timestamp has the same flaw in miniature: two bumps in the same millisecond are the same number. So
// the nonce is a plain counter that never reads React state at all, and is therefore never wrong however
// many times it is called between renders.
let nonce = 0;

/**
 * "The files on disk changed — anyone showing them should re-read." Subscribers name the signal as an
 * effect dependency; publishers call `bump` after a write, a delete, an upload or an agent turn that
 * touched the filesystem.
 */
export const useFilesRefresh = () => {
  const [signal, setSignal] = useFilesRefreshChannel();

  // `useGlobal`'s setter is a fresh closure on every render, so a `bump` built directly on it would be
  // too — and it goes into dependency lists. Reading through a ref makes the identity stable without
  // freezing the setter from the first render.
  const setRef = useRef(setSignal);
  setRef.current = setSignal;
  const bump = useCallback(() => setRef.current(++nonce), []);

  return [signal, bump] as const;
};

/**
 * Which conversation the Chat History detail pane is showing. This one is a genuine exception to
 * "selection lives in the URL": the route (`/chat/:sessionId`) is already the source of truth for *which*
 * session, and this carries the transcript the list has already loaded so the detail pane does not fetch
 * it a second time. §5.8 covers finishing the job.
 */
export const useSelectedChatSession = defineChannel<SelectedSession>('chat:selected-session', null);

/**
 * Which session the chat panel is currently on.
 *
 * **No subscriber today.** `ChatPanelWrapper` publishes it and nothing reads it. It is declared here
 * rather than deleted because removing the publisher changes the chat panel's own prop plumbing, and the
 * chat is not this branch's to change — see `COMMS/chat-agent-handoff-2026-08-07.md`.
 */
export const useActiveChatSession = defineChannel<string | null>('chat:active-session', null);

/**
 * "An agent turn touched something a preview is showing."
 *
 * **No subscriber today either**, and unlike the one above it has no plausible one: the `PreviewProvider`
 * that read it no longer exists anywhere in the repo. Same reason for declaring rather than deleting.
 */
export const usePreviewRefresh = defineChannel<number>('preview:refresh', 0);
