import type { ChatMessage } from '../types';

export type TitleMode = 'rotate' | 'prefix';

export interface TitleRotatorOptions {
  /** Snapshot taken on first `start()` if omitted. */
  base?: string;
  /**
   * Build the alert string for the current unread state.
   * Only used in `rotate` mode. Default: `"(N) Base"` — same shape as
   * the prefix mode, so the two modes differ only in *whether* the
   * title rotates, not in what it says.
   */
  template?: (count: number, latest?: ChatMessage | null, base?: string) => string;
  /** Only used in `rotate` mode. Default 2000ms — gentle, not nervous. */
  intervalMs?: number;
  /**
   * - `prefix` (default): render `"(N) Base"` once, no interval. The
   *   restrained Facebook-style cue: rely on the favicon dot for
   *   attention, keep the title readable.
   * - `rotate`: swap between base and `template(...)` every
   *   `intervalMs`. Use when the host has no favicon (or the user
   *   pinned the tab and favicons are hidden).
   */
  mode?: TitleMode;
}

interface RotatorHandle {
  start(count: number, latest?: ChatMessage | null): void;
  update(count: number, latest?: ChatMessage | null): void;
  stop(): void;
}

const DEFAULT_TEMPLATE = (count: number, _latest?: ChatMessage | null, base?: string) =>
  base ? `(${count}) ${base}` : `(${count})`;

/**
 * Mutates `document.title`. SSR-safe (returns no-op handle).
 *
 * Invariants:
 *   - `stop()` always restores the base title.
 *   - Multiple `start()` calls without an intervening `stop()` are
 *     equivalent to `update()`; we never stack timers.
 *   - In `rotate` mode the timer only ticks when the page is hidden
 *     (the caller is expected to gate this; see `useChatUnreadNotifier`).
 */
export function createTitleRotator(opts: TitleRotatorOptions = {}): RotatorHandle {
  if (typeof document === 'undefined') {
    return { start() {}, update() {}, stop() {} };
  }

  const template = opts.template ?? DEFAULT_TEMPLATE;
  const intervalMs = opts.intervalMs ?? 2000;
  const mode: TitleMode = opts.mode ?? 'prefix';

  let baseTitle: string | null = null;
  let alertTitle = '';
  let phase: 'base' | 'alert' = 'base';
  let timer: ReturnType<typeof setInterval> | null = null;

  const captureBaseOnce = () => {
    if (baseTitle !== null) return;
    baseTitle = opts.base ?? document.title;
  };

  const render = () => {
    if (baseTitle === null) return;
    document.title = phase === 'alert' ? alertTitle : baseTitle;
  };

  const tick = () => {
    phase = phase === 'alert' ? 'base' : 'alert';
    render();
  };

  const prefixTitle = (count: number) =>
    baseTitle ? `(${count}) ${baseTitle}` : `(${count})`;

  return {
    start(count, latest) {
      captureBaseOnce();
      alertTitle = template(count, latest, baseTitle ?? undefined);

      if (mode === 'prefix') {
        document.title = prefixTitle(count);
        return;
      }

      phase = 'alert';
      render();
      if (timer === null) {
        timer = setInterval(tick, intervalMs);
      }
    },

    update(count, latest) {
      // Same path as start — base title is already captured, just refresh
      // the alert string and re-render in case we're in alert phase.
      captureBaseOnce();
      alertTitle = template(count, latest, baseTitle ?? undefined);
      if (mode === 'prefix') {
        document.title = prefixTitle(count);
      } else if (phase === 'alert') {
        render();
      }
    },

    stop() {
      if (timer !== null) {
        clearInterval(timer);
        timer = null;
      }
      if (baseTitle !== null) {
        document.title = baseTitle;
      }
      phase = 'base';
    },
  };
}
