'use client';

import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
import { GameConnection, type ConnectionOptions, type GameViewState } from './connection';

/** The game server URL the browser should use. */
export function gameServerUrl(): string {
  return process.env.NEXT_PUBLIC_GAME_SERVER_URL || 'http://localhost:4000';
}

const EMPTY: GameViewState = {
  status: 'connecting',
  snapshot: null,
  public: null,
  me: null,
  host: null,
  lastType: null,
  messageCount: 0,
  lastData: null,
  lastDataType: null,
  seq: 0,
  offsetMs: 0,
  error: null,
  kicked: false,
};
const noopSubscribe = () => () => undefined;
const emptySnapshot = () => EMPTY;

/**
 * Opens one connection for the lifetime of the component (or until `key` changes) and returns its
 * live state. Options are read through a ref so callers can pass inline callbacks.
 */
export function useGameConnection(options: Omit<ConnectionOptions, 'url'> & { url?: string }, key: string | null) {
  const optsRef = useRef(options);
  optsRef.current = options;
  const [conn, setConn] = useState<GameConnection | null>(null);

  useEffect(() => {
    if (key === null) return;
    const c = new GameConnection({
      url: optsRef.current.url ?? gameServerUrl(),
      auth: () => optsRef.current.auth(),
      onToken: (d) => optsRef.current.onToken?.(d),
      onAuthError: (e) => optsRef.current.onAuthError?.(e),
    });
    setConn(c);
    c.connect();
    return () => {
      c.disconnect();
      setConn(null);
    };
  }, [key]);

  const state = useSyncExternalStore(conn?.subscribe ?? noopSubscribe, conn?.getState ?? emptySnapshot, emptySnapshot);
  return { conn, state };
}

/** Re-renders every `intervalMs` with the current SERVER time (for countdowns). */
export function useServerNow(conn: GameConnection | null, intervalMs = 100): number {
  const [now, setNow] = useState(() => (conn ? conn.serverNow() : Date.now()));
  useEffect(() => {
    const tick = () => setNow(conn ? conn.serverNow() : Date.now());
    tick();
    const id = setInterval(tick, intervalMs);
    return () => clearInterval(id);
  }, [conn, intervalMs]);
  return now;
}

/** Milliseconds left until a server timestamp (never negative). */
export function useCountdown(conn: GameConnection | null, endsAt: number | null | undefined, intervalMs = 100): number {
  const now = useServerNow(conn, intervalMs);
  return endsAt ? Math.max(0, endsAt - now) : 0;
}

/** Keep a screen awake (TVs, phones mid-game) where the Wake Lock API exists. */
export function useWakeLock(enabled: boolean): void {
  useEffect(() => {
    if (!enabled || typeof navigator === 'undefined' || !('wakeLock' in navigator)) return;
    let lock: WakeLockSentinel | null = null;
    let cancelled = false;
    const acquire = async () => {
      try {
        lock = await navigator.wakeLock.request('screen');
      } catch {
        // denied or unsupported (e.g. battery saver) — nothing else to do
      }
    };
    const onVisible = () => {
      if (document.visibilityState === 'visible' && !cancelled) void acquire();
    };
    void acquire();
    document.addEventListener('visibilitychange', onVisible);
    return () => {
      cancelled = true;
      document.removeEventListener('visibilitychange', onVisible);
      void lock?.release().catch(() => undefined);
    };
  }, [enabled]);
}
