import { useEffect, useRef } from 'react';

const ACTIVITY_EVENTS = [
  'mousedown',
  'mousemove',
  'keydown',
  'scroll',
  'touchstart',
  'click',
] as const;

/**
 * Fires `onIdle` once no activity event has fired for `timeoutMs` — the
 * "left the desk" auto-logout for a govt app handling sensitive records.
 *
 * `onIdle` is read through a ref rather than a `useEffect` dependency: the
 * caller typically passes a fresh closure every render (it captures
 * `dispatch`, `navigate`, current user), and depending on it directly would
 * tear down and re-add six window listeners — and rearm the timer to a full
 * 15 minutes — on every unrelated re-render, not just real activity.
 */
export function useIdleTimer(timeoutMs: number, onIdle: () => void) {
  const onIdleRef = useRef(onIdle);
  onIdleRef.current = onIdle;

  useEffect(() => {
    let timer: ReturnType<typeof setTimeout>;
    let lastReset = 0;

    // localStorage is origin-scoped, so every tab of this app shares it and
    // the sibling app (other port/domain) does not.
    const ACTIVITY_KEY = 'ema-last-activity';

    function fire() {
      // This tab sat idle, but a sibling tab may have been busy the whole
      // time — logging out here would clear the shared cookies and kill that
      // tab mid-work. Trust the newest activity stamp any tab wrote.
      let last = 0;
      try {
        last = Number(localStorage.getItem(ACTIVITY_KEY)) || 0;
      } catch {
        /* storage blocked — fall back to this tab's own timer */
      }
      const remaining = last + timeoutMs - Date.now();
      if (remaining > 1000) {
        timer = setTimeout(fire, remaining);
      } else {
        onIdleRef.current();
      }
    }

    function reset() {
      // mousemove fires dozens of times a second; only rearm once a second
      // so it isn't clearing/setting a timeout on every pixel of movement.
      const now = Date.now();
      if (now - lastReset < 1000) return;
      lastReset = now;
      try {
        localStorage.setItem(ACTIVITY_KEY, String(now));
      } catch {
        /* ignore */
      }
      clearTimeout(timer);
      timer = setTimeout(fire, timeoutMs);
    }

    reset();
    ACTIVITY_EVENTS.forEach((event) => window.addEventListener(event, reset));
    return () => {
      ACTIVITY_EVENTS.forEach((event) =>
        window.removeEventListener(event, reset),
      );
      clearTimeout(timer);
    };
  }, [timeoutMs]);
}
