import { useEffect, useRef } from 'react';

/**
 * Extended Navigator type that includes the Screen Wake Lock API
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API
 */
type WakeLockCapableNavigator = Navigator & {
  wakeLock?: {
    request: (type: WakeLockType) => Promise<WakeLockSentinel>;
  };
};

/**
 * Checks if we're in a client-side environment (browser)
 * Prevents SSR issues by verifying window, navigator, and document exist
 */
const isClientEnvironment =
  typeof window !== 'undefined' &&
  typeof navigator !== 'undefined' &&
  typeof document !== 'undefined';

const getNavigator = () => navigator as WakeLockCapableNavigator;

/**
 * Determines if the browser supports the Screen Wake Lock API
 * Checking outside component scope for better performance
 */
const supportsWakeLock = isClientEnvironment && 'wakeLock' in navigator;

/**
 * Enable/disable debug logging for wake lock operations
 * Set to true during development to see wake lock lifecycle events
 */
const DEBUG_WAKE_LOCK = false;

/**
 * Custom hook to prevent screen from sleeping during critical operations
 * Uses the Screen Wake Lock API to keep the device screen active
 *
 * @param shouldHold - Boolean flag indicating whether to acquire/hold the wake lock
 * @returns void - This hook manages wake lock state internally
 *
 * @example
 * ```tsx
 * const isGeneratingResponse = useRecoilValue(anySubmittingSelector);
 * useWakeLock(isGeneratingResponse);
 * ```
 *
 * @remarks
 * - Automatically handles page visibility changes (reacquires lock when tab becomes visible)
 * - Properly cleans up lock on unmount or when shouldHold becomes false
 * - Gracefully degrades on browsers without Wake Lock API support
 * - Wake locks are automatically released when user switches tabs
 */
export const useWakeLock = (shouldHold: boolean) => {
  const wakeLockRef = useRef<WakeLockSentinel | null>(null);

  useEffect(() => {
    if (!supportsWakeLock) {
      if (DEBUG_WAKE_LOCK) {
        console.log('[WakeLock] API not supported in this browser');
      }
      return;
    }

    /**
     * Flag to prevent operations after effect cleanup
     * Essential for avoiding race conditions when:
     * - Component unmounts while lock is being acquired
     * - shouldHold changes while async operations are in flight
     * - Multiple visibility change events fire in quick succession
     */
    let cancelled = false;
    const { wakeLock } = getNavigator();

    if (!wakeLock) {
      return;
    }

    /**
     * Releases the currently held wake lock
     * Called when: shouldHold becomes false, component unmounts, or before acquiring new lock
     */
    const releaseLock = async () => {
      if (!wakeLockRef.current) {
        return;
      }

      try {
        await wakeLockRef.current.release();
        if (DEBUG_WAKE_LOCK) {
          console.log('[WakeLock] Lock released successfully');
        }
      } catch (error) {
        console.warn('[WakeLock] release failed', error);
      } finally {
        wakeLockRef.current = null;
      }
    };

    /**
     * Requests a new wake lock from the browser
     * Checks multiple conditions before requesting to avoid unnecessary API calls:
     * - shouldHold must be true (user wants lock)
     * - cancelled must be false (effect still active)
     * - document must be visible (API requirement - locks only work in visible tabs)
     * - no existing lock (prevent duplicate locks)
     */
    const requestLock = async () => {
      if (
        !shouldHold ||
        cancelled ||
        document.visibilityState !== 'visible' ||
        wakeLockRef.current
      ) {
        return;
      }

      try {
        const sentinel = await wakeLock.request('screen');
        wakeLockRef.current = sentinel;

        if (DEBUG_WAKE_LOCK) {
          console.log('[WakeLock] Lock acquired successfully');
        }

        /**
         * CRITICAL: Recursive re-acquire logic for automatic lock restoration
         *
         * The browser automatically releases wake locks when:
         * - User switches to a different tab
         * - User minimizes the browser window
         * - Device goes to sleep
         * - User navigates to a different page
         *
         * This handler automatically re-acquires the lock when:
         * 1. The lock is released by the browser
         * 2. The effect is still active (not cancelled)
         * 3. The component still wants to hold the lock (shouldHold is true)
         * 4. The tab is visible again (document.visibilityState === 'visible')
         *
         * This ensures users don't need to manually restart their work after
         * switching tabs during long-running operations like AI response generation.
         */
        const handleRelease = () => {
          wakeLockRef.current = null;
          sentinel.removeEventListener('release', handleRelease);

          if (DEBUG_WAKE_LOCK) {
            console.log('[WakeLock] Lock released, checking if re-acquire needed');
          }

          if (!cancelled && shouldHold && document.visibilityState === 'visible') {
            if (DEBUG_WAKE_LOCK) {
              console.log('[WakeLock] Re-acquiring lock');
            }
            void requestLock();
          }
        };

        sentinel.addEventListener('release', handleRelease);
      } catch (error) {
        console.warn('[WakeLock] request failed', error);
      }
    };

    /**
     * Handles browser tab visibility changes
     * When user returns to the tab, re-acquire the lock if it's still needed
     * This is necessary because wake locks are automatically released when tab becomes hidden
     */
    const handleVisibilityChange = () => {
      if (cancelled) {
        return;
      }

      if (DEBUG_WAKE_LOCK) {
        console.log('[WakeLock] Visibility changed:', document.visibilityState);
      }

      if (document.visibilityState === 'visible' && shouldHold) {
        void requestLock();
      }
    };

    if (shouldHold) {
      void requestLock();
      document.addEventListener('visibilitychange', handleVisibilityChange);
    } else {
      void releaseLock();
    }

    /**
     * Cleanup function runs when:
     * - Component unmounts
     * - shouldHold changes
     * - Effect dependencies change
     *
     * Sets cancelled flag first to prevent any in-flight operations from completing
     * Removes event listeners to prevent memory leaks
     * Releases any held wake lock
     */
    return () => {
      cancelled = true;
      document.removeEventListener('visibilitychange', handleVisibilityChange);
      void releaseLock();
    };
  }, [shouldHold]);
};

export default useWakeLock;
