'use client';

import { useAuth, useClerk } from '@clerk/nextjs';
import * as Ably from 'ably';
import { AblyProvider } from 'ably/react';
import React, { PropsWithChildren, useEffect, useRef, useState } from 'react';

import SpinnerSVG from '@/components/svg/SpinnerSVG';

import { getModalBaseUrl } from './utils';

export const OrpheusAblyProvider: React.FC<PropsWithChildren> = ({
  children,
}) => {
  const { getToken } = useAuth();
  const clerk = useClerk();
  const [ablyClient, setAblyClient] = useState<Ably.Realtime | null>(null);

  const modalBaseUrl = getModalBaseUrl();

  // Initialize refs outside useEffect so they persist across effect runs
  const currentClientRef = useRef<Ably.Realtime | null>(null);
  const visibilityHandlerRef = useRef<(() => void) | null>(null);
  const isInitializingRef = useRef(false);
  const recreateAttemptsRef = useRef(0);

  useEffect(() => {
    const MAX_RECREATE_ATTEMPTS = 5;

    const initAbly = async () => {
      // Atomic check-and-set to prevent race conditions
      if (isInitializingRef.current) {
        return;
      }
      isInitializingRef.current = true;

      try {
        recreateAttemptsRef.current++;

        // Prevent infinite recursion
        if (recreateAttemptsRef.current > MAX_RECREATE_ATTEMPTS) {
          console.error(
            'Max recreate attempts reached, aborting Ably initialization'
          );
          isInitializingRef.current = false;
          return;
        }

        const token = await getToken();
        const client = new Ably.Realtime({
          authUrl: `${modalBaseUrl}/ably-auth`,
          authHeaders: { Authorization: `Bearer ${token}` },
          clientId: `user:${clerk.session?.user.id ?? ''}`,
        });

        // Create handler that captures the SPECIFIC client that failed
        const failedHandler = () => {
          // Use the captured client, not the ref
          if (client) {
            client.connection.off('failed', failedHandler);
            client.close();
          }

          // Only recreate if this was the current client
          if (currentClientRef.current === client) {
            currentClientRef.current = null;
            // Small delay before recreating to avoid immediate re-attempt
            setTimeout(() => {
              if (!isInitializingRef.current) {
                initAbly();
              }
            }, 1000);
          }
        };

        client.connection.on('failed', failedHandler);

        // Update currentClient AFTER setting up handlers
        currentClientRef.current = client;
        setAblyClient(client);

        // Reset counter if connection succeeds
        client.connection.on('connected', () => {
          recreateAttemptsRef.current = 0;
        });

        // Set up visibility change handler
        const visibilityHandler = () => {
          if (
            !document.hidden &&
            currentClientRef.current &&
            ['suspended', 'failed', 'closed'].includes(
              currentClientRef.current.connection.state ?? ''
            )
          ) {
            // User-initiated reconnect: reset attempts and reconnect
            recreateAttemptsRef.current = 0;

            // Clean up the current client
            const clientToClose = currentClientRef.current;
            if (clientToClose) {
              clientToClose.connection.off();
              clientToClose.close();
              currentClientRef.current = null;
            }

            // Reconnect with fresh attempt counter
            if (!isInitializingRef.current) {
              initAbly();
            }
          }
        };

        visibilityHandlerRef.current = visibilityHandler;
        document.addEventListener(
          'visibilitychange',
          visibilityHandlerRef.current
        );

        isInitializingRef.current = false;
      } catch (error) {
        console.error('Failed to initialize Ably:', error);
        isInitializingRef.current = false;
        // Don't automatically retry on sync errors (like auth failure)
        if (recreateAttemptsRef.current <= MAX_RECREATE_ATTEMPTS) {
          setTimeout(() => {
            if (!isInitializingRef.current) {
              initAbly();
            }
          }, 2000);
        }
      }
    };

    // Reset state when user changes
    if (clerk.session?.user.id) {
      currentClientRef.current = null;
      recreateAttemptsRef.current = 0;
      isInitializingRef.current = false;
      if (visibilityHandlerRef.current) {
        document.removeEventListener(
          'visibilitychange',
          visibilityHandlerRef.current
        );
        visibilityHandlerRef.current = null;
      }
    }

    if (clerk.session?.user.id && !ablyClient) {
      initAbly();
    }

    return () => {
      // Cleanup visibility listener
      if (visibilityHandlerRef.current) {
        document.removeEventListener(
          'visibilitychange',
          visibilityHandlerRef.current
        );
      }

      // Cleanup Ably client
      if (currentClientRef.current) {
        currentClientRef.current.connection.off();
        currentClientRef.current.close();
        currentClientRef.current = null;
      }
      setAblyClient(null);
      isInitializingRef.current = false;
      recreateAttemptsRef.current = 0;
    };
  }, [getToken, clerk.session?.user.id]);

  if (!ablyClient)
    return (
      <div className='flex h-full w-full items-center justify-center'>
        <SpinnerSVG />
      </div>
    );

  return <AblyProvider client={ablyClient}>{children}</AblyProvider>;
};
