'use client';

import { useEffect, useMemo, useRef, useState } from 'react';

import { useActiveTabStore } from '@djangocfg/ui-core/hooks';

import {
  createBrowserNotifier,
  createCrossTabNotifier,
  isPageHidden,
  onVisibilityChange,
  type BrowserNotifierOptions,
  type ChatNotifier,
} from '../notifier';

import { useChatUnread, type UseChatUnreadOptions } from './useChatUnread';

export interface UseChatUnreadNotifierOptions extends UseChatUnreadOptions {
  /**
   * Custom notifier. Pass a host-specific implementation (Wails dock
   * badge etc.) to opt out of the built-in browser title/favicon
   * mutation. If omitted, a `createBrowserNotifier` instance is used.
   */
  notifier?: ChatNotifier;
  /**
   * Options forwarded to the default browser notifier. Ignored when an
   * explicit `notifier` is provided.
   */
  browser?: BrowserNotifierOptions;
  /**
   * Master switch. Default `true`. Set false to keep the unread
   * tracking but skip all environment mutation.
   */
  enabled?: boolean;
  /**
   * Cross-tab coordination. When enabled (default), only the elected
   * leader tab mutates `document.title` / favicon — other tabs stay
   * silent. The unread count is broadcast so every tab's FAB badge UI
   * still reflects reality.
   *
   * Pass `false` to disable; pass an options object to customise the
   * BroadcastChannel name. Disable in single-tab hosts (Wails / Electron)
   * where leadership is moot.
   */
  crossTab?: boolean | { channel?: string };
}

/**
 * Glue between `useChatUnread` and a `ChatNotifier`.
 *
 * Inputs that drive the notifier:
 *   1. `useChatUnread` — provider-state-derived `{ count, unread }`.
 *   2. Page visibility — clear when visible; re-arm when hidden+count>0.
 *   3. Tab leadership (when `crossTab` enabled) — only leader mutates
 *      title/favicon; followers receive count broadcasts so their
 *      in-tab badge UI stays in sync.
 *
 * Returns `useChatUnread`'s shape, with the `count` overridden by
 * cross-tab broadcasts when this tab is a follower (so the FAB badge
 * shows the same number across every tab).
 */
export function useChatUnreadNotifier(opts: UseChatUnreadNotifierOptions = {}) {
  const {
    notifier: notifierProp,
    browser,
    enabled = true,
    crossTab = true,
    ...unreadOpts
  } = opts;
  const unread = useChatUnread(unreadOpts);

  // Cross-tab count from peers (followers see this; leader publishes).
  const [peerCount, setPeerCount] = useState<number | null>(null);

  const crossTabChannel =
    typeof crossTab === 'object' ? crossTab.channel : undefined;
  const crossTabEnabled = crossTab !== false;

  // Build the notifier. Inner = host-supplied OR built-in browser.
  // Wrap with cross-tab decorator when enabled.
  const notifier = useMemo<ChatNotifier>(() => {
    const inner = notifierProp ?? createBrowserNotifier(browser);
    if (!crossTabEnabled) return inner;
    return createCrossTabNotifier({
      inner,
      isLeader: () => useActiveTabStore.getState().isLeader,
      channel: crossTabChannel,
      onPeerUpdate: (count) => setPeerCount(count),
    });
  }, [notifierProp, browser, crossTabEnabled, crossTabChannel]);

  const lastSyncedCount = useRef(0);

  // Visibility-driven sync. Single effect owns both the listener and
  // the imperative calls to keep ordering deterministic.
  useEffect(() => {
    if (!enabled) {
      notifier.clear();
      return;
    }

    const sync = () => {
      const hidden = isPageHidden();
      if (hidden && unread.count > 0) {
        notifier.setUnread(unread.count, unread.unread);
        lastSyncedCount.current = unread.count;
      } else {
        notifier.clear();
        lastSyncedCount.current = 0;
      }
    };

    sync();
    const unsub = onVisibilityChange(sync);
    return () => {
      unsub();
      notifier.clear();
    };
  }, [enabled, notifier, unread.count, unread.unread]);

  // Final cleanup — release any host-side resources.
  useEffect(() => () => notifier.dispose?.(), [notifier]);

  // Effective count: max of local (this tab's own unread tracking) and
  // peer broadcast. The max handles the case where a peer hasn't sent
  // a broadcast yet (peerCount === null) — we trust local.
  const effectiveCount =
    peerCount !== null ? Math.max(unread.count, peerCount) : unread.count;

  return {
    ...unread,
    count: effectiveCount,
  };
}
