'use client';

import React, { createContext, useContext, useEffect, useState, useCallback, useRef } from 'react';
import { HubConnection } from '@microsoft/signalr';
import { SignalRConnectionManager, ConnectionState } from './ConnectionManager';
import { SIGNALR_CONFIG } from './config';
import { useAuthStore } from '@/stores/authStore';
import { toast } from 'sonner';
import { logger } from '@/lib/utils/logger';

// ============================================
// TYPE DEFINITIONS
// ============================================

interface SignalRContextValue {
  // Connection management
  connection: HubConnection | null;
  connectionState: ConnectionState;
  isConnected: boolean;

  // Event handlers registry
  service: SignalREventService | null;

  // Manual connection control (optional)
  connect: () => Promise<void>;
  disconnect: () => Promise<void>;
}

// Event subscription service
interface SignalREventService {
  subscribe: (eventName: string, handler: (...args: any[]) => void) => () => void;
  unsubscribe: (eventName: string, handler?: (...args: any[]) => void) => void;
  getEventHandlers: () => SignalREventService | null;
}

// ============================================
// CONTEXT CREATION
// ============================================

const SignalRContext = createContext<SignalRContextValue | null>(null);

// ============================================
// PROVIDER COMPONENT
// ============================================

interface SignalRProviderProps {
  children: React.ReactNode;
  hubUrl?: string; // Optional: custom hub URL (defaults to PROJECT hub)
  autoConnect?: boolean; // Auto-connect when authenticated (default: true)
  showToasts?: boolean; // Show connection status toasts (default: true)
}

export function SignalRProvider({
  children,
  hubUrl = SIGNALR_CONFIG.HUB_URLS.PROJECT,
  autoConnect = true,
  showToasts = true,
}: SignalRProviderProps) {
  const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
  const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');
  const managerRef = useRef<SignalRConnectionManager | null>(null);
  const eventHandlersRef = useRef<Map<string, Set<(...args: any[]) => void>>>(new Map());

  // ============================================
  // EVENT SERVICE IMPLEMENTATION
  // ============================================

  const eventService: SignalREventService = {
    subscribe: (eventName: string, handler: (...args: any[]) => void) => {
      // Register handler
      if (!eventHandlersRef.current.has(eventName)) {
        eventHandlersRef.current.set(eventName, new Set());
      }
      eventHandlersRef.current.get(eventName)?.add(handler);

      // Subscribe to SignalR event
      if (managerRef.current) {
        managerRef.current.on(eventName, handler);
      }

      // Return unsubscribe function
      return () => {
        eventHandlersRef.current.get(eventName)?.delete(handler);
        if (managerRef.current) {
          managerRef.current.off(eventName, handler);
        }
      };
    },

    unsubscribe: (eventName: string, handler?: (...args: any[]) => void) => {
      if (handler) {
        eventHandlersRef.current.get(eventName)?.delete(handler);
        if (managerRef.current) {
          managerRef.current.off(eventName, handler);
        }
      } else {
        // Unsubscribe all handlers for this event
        eventHandlersRef.current.delete(eventName);
        if (managerRef.current) {
          managerRef.current.off(eventName);
        }
      }
    },

    getEventHandlers: () => eventService,
  };

  // ============================================
  // CONNECTION MANAGEMENT
  // ============================================

  const connect = useCallback(async () => {
    if (!isAuthenticated) {
      logger.warn('[SignalRContext] Cannot connect: user not authenticated');
      return;
    }

    if (managerRef.current?.state === 'connected') {
      logger.debug('[SignalRContext] Already connected');
      return;
    }

    const manager = new SignalRConnectionManager(hubUrl);
    managerRef.current = manager;

    // Subscribe to state changes
    manager.onStateChange((state) => {
      setConnectionState(state);

      // Show toast notifications
      if (showToasts) {
        if (state === 'connected') {
          toast.success('Connected to real-time updates');
        } else if (state === 'disconnected') {
          toast.error('Disconnected from real-time updates');
        } else if (state === 'reconnecting') {
          toast.info('Reconnecting...');
        }
      }
    });

    // Re-subscribe all registered event handlers
    eventHandlersRef.current.forEach((handlers, eventName) => {
      handlers.forEach((handler) => {
        manager.on(eventName, handler);
      });
    });

    try {
      await manager.start();
    } catch (error) {
      logger.error('[SignalRContext] Connection error:', error);
      if (showToasts) {
        toast.error('Failed to connect to real-time updates');
      }
    }
  }, [isAuthenticated, hubUrl, showToasts]);

  const disconnect = useCallback(async () => {
    if (managerRef.current) {
      await managerRef.current.stop();
      managerRef.current = null;
    }
  }, []);

  // ============================================
  // AUTO-CONNECT ON AUTHENTICATION
  // ============================================

  useEffect(() => {
    if (autoConnect && isAuthenticated) {
      connect();
    } else if (!isAuthenticated) {
      disconnect();
    }

    return () => {
      disconnect();
    };
  }, [isAuthenticated, autoConnect, connect, disconnect]);

  // ============================================
  // CONTEXT VALUE
  // ============================================

  const contextValue: SignalRContextValue = {
    connection: managerRef.current?.['connection'] ?? null,
    connectionState,
    isConnected: connectionState === 'connected',
    service: eventService,
    connect,
    disconnect,
  };

  return (
    <SignalRContext.Provider value={contextValue}>
      {children}
    </SignalRContext.Provider>
  );
}

// ============================================
// CUSTOM HOOKS
// ============================================

/**
 * Access SignalR context
 */
export function useSignalRContext(): SignalRContextValue {
  const context = useContext(SignalRContext);
  if (!context) {
    throw new Error('useSignalRContext must be used within SignalRProvider');
  }
  return context;
}

/**
 * Subscribe to a specific SignalR event (simplified hook)
 *
 * @example
 * useSignalREvent('TaskStatusChanged', (taskId, newStatus) => {
 *   console.log('Task status changed:', taskId, newStatus);
 * });
 */
export function useSignalREvent(
  eventName: string,
  handler: (...args: any[]) => void,
  deps: React.DependencyList = []
) {
  const { service, isConnected } = useSignalRContext();

  useEffect(() => {
    if (!isConnected || !service) {
      return;
    }

    const unsubscribe = service.subscribe(eventName, handler);

    return () => {
      unsubscribe();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [eventName, isConnected, service, ...deps]);
}

/**
 * Subscribe to multiple SignalR events
 *
 * @example
 * useSignalREvents({
 *   'TaskCreated': (task) => console.log('Task created:', task),
 *   'TaskUpdated': (task) => console.log('Task updated:', task),
 * });
 */
export function useSignalREvents(
  events: Record<string, (...args: any[]) => void>,
  deps: React.DependencyList = []
) {
  const { service, isConnected } = useSignalRContext();

  useEffect(() => {
    if (!isConnected || !service) {
      return;
    }

    const unsubscribers = Object.entries(events).map(([eventName, handler]) =>
      service.subscribe(eventName, handler)
    );

    return () => {
      unsubscribers.forEach((unsubscribe) => unsubscribe());
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isConnected, service, ...deps]);
}

/**
 * Get connection state and status
 */
export function useSignalRConnection() {
  const { connectionState, isConnected, connect, disconnect } = useSignalRContext();

  return {
    connectionState,
    isConnected,
    connect,
    disconnect,
  };
}
