'use client';

import { useEffect, useRef, useCallback } from 'react';
import { useSession } from 'next-auth/react';
import { socketService } from '@/services';

// Disconnect after 3 minutes of tab being hidden
const VISIBILITY_TIMEOUT = 3 * 60 * 1000;
// Disconnect after 5 minutes of no user interaction
const IDLE_TIMEOUT = 5 * 60 * 1000;

/**
 * PresenceProvider handles user online/offline presence globally.
 * - Connects socket on login (marks user online on the backend)
 * - Disconnects socket when tab is hidden for too long or user is idle
 * - Handles auth error recovery (token refresh + reconnect)
 */
export function PresenceProvider({ children }: { children: React.ReactNode }) {
  const { data: session, status } = useSession();
  const idleTimerRef = useRef<NodeJS.Timeout | null>(null);
  const visibilityTimerRef = useRef<NodeJS.Timeout | null>(null);
  const isIdleRef = useRef(false);
  const isConnectedRef = useRef(false);
  const hasInitializedRef = useRef(false);

  const getAccessToken = useCallback(() => {
    if (typeof window !== 'undefined') {
      return localStorage.getItem('accessToken');
    }
    return null;
  }, []);

  const connectSocket = useCallback(async () => {
    const token = getAccessToken();
    if (!token) return;

    try {
      await socketService.connect(token);
    } catch (error) {
      console.error('[Presence] Socket connect failed:', error);
    }
  }, [getAccessToken]);

  const disconnectForIdle = useCallback(() => {
    if (!isConnectedRef.current) return;
    isIdleRef.current = true;
    // Use pause() instead of disconnect() so subscriber handlers
    // (useMessaging listeners) survive the reconnect. Otherwise the
    // user would have to refresh the page after returning from idle
    // to see new messages in real time again.
    socketService.pause();
  }, []);

  // Track socket connection state
  useEffect(() => {
    const unsubscribe = socketService.onConnectionChange((connected) => {
      isConnectedRef.current = connected;
    });
    return unsubscribe;
  }, []);

  // Connect on authentication
  useEffect(() => {
    if (status === 'authenticated' && session?.user && !hasInitializedRef.current) {
      hasInitializedRef.current = true;
      // Small delay to ensure TokenSync has set localStorage tokens
      const timer = setTimeout(connectSocket, 500);
      return () => clearTimeout(timer);
    }

    if (status === 'unauthenticated') {
      hasInitializedRef.current = false;
      if (isConnectedRef.current) {
        socketService.disconnect();
      }
    }
  }, [status, session, connectSocket]);

  // Handle auth errors (token expiry) — single global handler
  useEffect(() => {
    const unsubscribe = socketService.onAuthError(async () => {
      // Skip token refresh if logout is in progress
      if (typeof window !== 'undefined' && localStorage.getItem('isLoggingOut') === 'true') {
        console.log('[Presence] Auth error during logout, skipping refresh');
        return;
      }

      console.log('[Presence] Auth error, refreshing token...');
      try {
        const refreshToken = localStorage.getItem('refreshToken');
        if (!refreshToken) return;

        const res = await fetch(
          `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1'}/auth/refresh`,
          {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ refreshToken }),
          }
        );
        const data = await res.json();
        if (res.ok && data.success) {
          localStorage.setItem('accessToken', data.data.accessToken);
          localStorage.setItem('refreshToken', data.data.refreshToken);
          socketService.reconnectWithToken(data.data.accessToken);
        } else {
          console.error('[Presence] Token refresh failed:', data.message);
        }
      } catch (error) {
        console.error('[Presence] Token refresh error:', error);
      }
    });
    return unsubscribe;
  }, []);

  // Browser visibility tracking — disconnect when tab hidden for too long
  useEffect(() => {
    const handleVisibilityChange = () => {
      if (document.hidden) {
        visibilityTimerRef.current = setTimeout(() => {
          if (document.hidden) {
            disconnectForIdle();
          }
        }, VISIBILITY_TIMEOUT);
      } else {
        // Tab visible again — clear timer and reconnect if was idle
        if (visibilityTimerRef.current) {
          clearTimeout(visibilityTimerRef.current);
          visibilityTimerRef.current = null;
        }
        if (isIdleRef.current) {
          isIdleRef.current = false;
          connectSocket();
        }
      }
    };

    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
      if (visibilityTimerRef.current) {
        clearTimeout(visibilityTimerRef.current);
      }
    };
  }, [connectSocket, disconnectForIdle]);

  // User activity tracking — disconnect when idle for too long
  useEffect(() => {
    const resetIdleTimer = () => {
      if (idleTimerRef.current) {
        clearTimeout(idleTimerRef.current);
      }

      // Was idle and user became active — reconnect
      if (isIdleRef.current) {
        isIdleRef.current = false;
        connectSocket();
      }

      idleTimerRef.current = setTimeout(disconnectForIdle, IDLE_TIMEOUT);
    };

    const events = ['mousedown', 'mousemove', 'keydown', 'scroll', 'touchstart'];
    events.forEach((e) => document.addEventListener(e, resetIdleTimer, { passive: true }));
    resetIdleTimer();

    return () => {
      events.forEach((e) => document.removeEventListener(e, resetIdleTimer));
      if (idleTimerRef.current) {
        clearTimeout(idleTimerRef.current);
      }
    };
  }, [connectSocket, disconnectForIdle]);

  return <>{children}</>;
}
