'use client';

import { useCallback, useEffect, useRef } from 'react';
import {
  AUTH_SESSION_KEY,
  AUTH_TOKEN_KEY,
  fetchAuthSession,
  isPinRequiredError,
} from '@/lib/api';

const ACTIVITY_EVENTS = ['mousedown', 'keydown', 'touchstart', 'scroll', 'wheel', 'pointerdown', 'click'] as const;
const ACTIVITY_DEBOUNCE_MS = 250;
const SERVER_TOUCH_INTERVAL_MS = 30_000;

interface UsePinIdleLockOptions {
  enabled: boolean;
  timeoutMinutes: number;
  accessToken: string | null;
  onLockRequired: (sessionId: string) => void;
}

export function usePinIdleLock({
  enabled,
  timeoutMinutes,
  accessToken,
  onLockRequired,
}: UsePinIdleLockOptions) {
  const onLockRequiredRef = useRef(onLockRequired);
  const accessTokenRef = useRef(accessToken);
  const timeoutMsRef = useRef(Math.max(1, timeoutMinutes) * 60_000);
  const idleDeadlineRef = useRef<number>(Date.now() + timeoutMsRef.current);
  const idleTimerRef = useRef<number | null>(null);
  const activityDebounceRef = useRef<number | null>(null);
  const lastServerTouchRef = useRef(0);
  const checkInFlightRef = useRef(false);
  const enabledRef = useRef(enabled);

  useEffect(() => {
    onLockRequiredRef.current = onLockRequired;
  }, [onLockRequired]);

  useEffect(() => {
    accessTokenRef.current = accessToken;
  }, [accessToken]);

  useEffect(() => {
    enabledRef.current = enabled;
  }, [enabled]);

  useEffect(() => {
    timeoutMsRef.current = Math.max(1, timeoutMinutes) * 60_000;
    if (enabledRef.current) {
      idleDeadlineRef.current = Date.now() + timeoutMsRef.current;
    }
  }, [timeoutMinutes]);

  const clearIdleTimer = useCallback(() => {
    if (idleTimerRef.current !== null) {
      window.clearTimeout(idleTimerRef.current);
      idleTimerRef.current = null;
    }
  }, []);

  const syncServerActivity = useCallback(async () => {
    const token = accessTokenRef.current ?? window.localStorage.getItem(AUTH_TOKEN_KEY);
    if (!token) return;

    const now = Date.now();
    if (now - lastServerTouchRef.current < SERVER_TOUCH_INTERVAL_MS) return;
    lastServerTouchRef.current = now;

    try {
      await fetchAuthSession(token);
    } catch (error) {
      if (isPinRequiredError(error)) {
        onLockRequiredRef.current(error.sessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY) ?? '');
      }
    }
  }, []);

  const checkIdleLock = useCallback(async () => {
    if (!enabledRef.current || checkInFlightRef.current) return;

    const remainingMs = idleDeadlineRef.current - Date.now();
    if (remainingMs > 0) {
      clearIdleTimer();
      idleTimerRef.current = window.setTimeout(() => {
        void checkIdleLock();
      }, remainingMs);
      return;
    }

    checkInFlightRef.current = true;
    clearIdleTimer();

    const token = accessTokenRef.current ?? window.localStorage.getItem(AUTH_TOKEN_KEY);
    if (!token) {
      checkInFlightRef.current = false;
      return;
    }

    try {
      const session = await fetchAuthSession(token);
      if (session.requiresPin) {
        onLockRequiredRef.current(session.sessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY) ?? '');
        return;
      }

      idleDeadlineRef.current = Date.now() + timeoutMsRef.current;
      clearIdleTimer();
      idleTimerRef.current = window.setTimeout(() => {
        void checkIdleLock();
      }, timeoutMsRef.current);
    } catch (error) {
      if (isPinRequiredError(error)) {
        onLockRequiredRef.current(error.sessionId ?? window.localStorage.getItem(AUTH_SESSION_KEY) ?? '');
        return;
      }

      idleDeadlineRef.current = Date.now() + timeoutMsRef.current;
      clearIdleTimer();
      idleTimerRef.current = window.setTimeout(() => {
        void checkIdleLock();
      }, timeoutMsRef.current);
    } finally {
      checkInFlightRef.current = false;
    }
  }, [clearIdleTimer]);

  const registerActivity = useCallback(() => {
    if (!enabledRef.current) return;

    idleDeadlineRef.current = Date.now() + timeoutMsRef.current;
    clearIdleTimer();
    idleTimerRef.current = window.setTimeout(() => {
      void checkIdleLock();
    }, timeoutMsRef.current);

    void syncServerActivity();
  }, [checkIdleLock, clearIdleTimer, syncServerActivity]);

  const scheduleActivityReset = useCallback(() => {
    if (activityDebounceRef.current !== null) {
      window.clearTimeout(activityDebounceRef.current);
    }
    activityDebounceRef.current = window.setTimeout(() => {
      activityDebounceRef.current = null;
      registerActivity();
    }, ACTIVITY_DEBOUNCE_MS);
  }, [registerActivity]);

  useEffect(() => {
    if (!enabled) {
      clearIdleTimer();
      if (activityDebounceRef.current !== null) {
        window.clearTimeout(activityDebounceRef.current);
        activityDebounceRef.current = null;
      }
      return;
    }

    idleDeadlineRef.current = Date.now() + timeoutMsRef.current;
    lastServerTouchRef.current = Date.now();
    void checkIdleLock();

    for (const eventName of ACTIVITY_EVENTS) {
      window.addEventListener(eventName, scheduleActivityReset, { passive: true });
    }

    function handleVisibilityChange() {
      if (document.visibilityState !== 'visible') {
        clearIdleTimer();
        return;
      }
      void checkIdleLock();
    }

    document.addEventListener('visibilitychange', handleVisibilityChange);

    return () => {
      clearIdleTimer();
      if (activityDebounceRef.current !== null) {
        window.clearTimeout(activityDebounceRef.current);
        activityDebounceRef.current = null;
      }
      for (const eventName of ACTIVITY_EVENTS) {
        window.removeEventListener(eventName, scheduleActivityReset);
      }
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, [checkIdleLock, clearIdleTimer, enabled, scheduleActivityReset]);
}
