import { createContext, useContext, useState, useCallback, useEffect, useMemo } from 'react';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { CallService } from './CallService';
import { ActiveCall, CallEvent, CallEventType, CallState, CallType } from './types';
import { fetchWellKnownWithRTC, getLiveKitFocus, getLiveKitHomeserverPriority } from './useRtcConfig';

interface CallContextValue {
  callService: CallService | null;
  activeCall: ActiveCall | null;
  callSupported: boolean;
  callSupportLoading: boolean;
  startCall: (roomId: string, callType: CallType) => Promise<void>;
  endCall: () => Promise<void>;
  toggleMute: () => boolean;
  toggleDeafen: () => boolean;
  toggleVideo: () => boolean;
  toggleScreenShare: () => Promise<boolean>;
}

const CallContext = createContext<CallContextValue | null>(null);

/**
 * Global reference to CallService for use outside React components
 * (e.g., from Electron IPC handlers)
 */
let globalCallServiceInstance: CallService | null = null;

/**
 * Get the global CallService instance
 * @returns The CallService instance or null if not initialized
 */
export function getCallService(): CallService | null {
  return globalCallServiceInstance;
}

/**
 * Set the global CallService instance
 * @param service - The CallService instance
 */
function setGlobalCallService(service: CallService | null): void {
  globalCallServiceInstance = service;
}

/**
 * Hook to access the call context
 * @returns The call context value
 */
export function useCall(): CallContextValue {
  const context = useContext(CallContext);
  if (!context) {
    throw new Error('useCall must be used within a CallProvider');
  }
  return context;
}

export const CallProvider = CallContext.Provider;

/**
 * Hook to initialize and manage the call service
 * Should be used at the app level to provide call context
 */
export function useCallService(): CallContextValue {
  const mx = useMatrixClient();
  const [callService, setCallService] = useState<CallService | null>(null);
  const [activeCall, setActiveCall] = useState<ActiveCall | null>(null);
  const [callSupported, setCallSupported] = useState(false);
  const [callSupportLoading, setCallSupportLoading] = useState(true);

  useEffect(() => {
    let unsubscribe: (() => void) | undefined;
    let service: CallService | undefined;

    const initCallService = async () => {
      const baseUrl = mx.getHomeserverUrl();
      const accessToken = mx.getAccessToken();
      const userId = mx.getSafeUserId();
      const deviceId = mx.getDeviceId();

      if (!accessToken || !deviceId) {
        setCallSupportLoading(false);
        return;
      }

      try {
        // Initialize CallService without checking user's homeserver
        // Call support will be determined per-room based on available homeservers
        console.log('Call service init - Initializing (call support determined per-room)');

        service = new CallService(
          {
            homeserverBaseUrl: baseUrl,
            accessToken,
            userId,
            deviceId,
          },
          mx
        );

        setCallService(service);
        setGlobalCallService(service); // Make available globally for Paarrot API
        setCallSupported(true); // Global flag - actual support checked per-room
        setCallSupportLoading(false);
        // eslint-disable-next-line no-console
        console.log('Call service init - Success!');

        unsubscribe = service.on((event: CallEvent) => {
          // Update state for any event that might change the call state
          if (event.type === CallEventType.StateChanged ||
              event.type === CallEventType.ScreenShareStarted ||
              event.type === CallEventType.ScreenShareStopped ||
              event.type === CallEventType.RemoteStreamAdded ||
              event.type === CallEventType.RemoteStreamRemoved ||
              event.type === CallEventType.ParticipantJoined ||
              event.type === CallEventType.ParticipantLeft ||
              event.type === CallEventType.SpeakersChanged) {
            const newCall = service?.getActiveCall();
            if (newCall) {
              // Deep copy Maps and Sets to ensure React detects changes
              setActiveCall({
                ...newCall,
                remoteStreams: new Map(newCall.remoteStreams),
                remoteScreenTracks: new Map(newCall.remoteScreenTracks),
                remoteScreenStreams: new Map(newCall.remoteScreenStreams),
                activeSpeakers: new Set(newCall.activeSpeakers),
              });
            } else {
              setActiveCall(null);
            }
          }
        });
      } catch (error) {
        // eslint-disable-next-line no-console
        console.error('Call service init - Error:', error);
        setCallSupported(false);
        setCallSupportLoading(false);
      }
    };

    initCallService();

    return () => {
      unsubscribe?.();
      service?.dispose();
      setGlobalCallService(null); // Clear global reference
    };
  }, [mx]);

  const startCall = useCallback(
    async (roomId: string, callType: CallType) => {
      if (!callService) {
        throw new Error('Call service not initialized');
      }
      await callService.startCall(roomId, callType);
    },
    [callService]
  );

  const endCall = useCallback(async () => {
    if (!callService) {
      return;
    }
    await callService.endCall();
  }, [callService]);

  const toggleMute = useCallback(() => {
    if (!callService) {
      return false;
    }
    const newState = callService.toggleMute();
    const call = callService.getActiveCall();
    if (call) {
      setActiveCall({ ...call });
    }
    return newState;
  }, [callService]);

  const toggleDeafen = useCallback(() => {
    if (!callService) {
      return false;
    }
    const newState = callService.toggleDeafen();
    const call = callService.getActiveCall();
    if (call) {
      setActiveCall({ ...call });
    }
    return newState;
  }, [callService]);

  const toggleVideo = useCallback(() => {
    if (!callService) {
      return false;
    }
    const newState = callService.toggleVideo();
    const call = callService.getActiveCall();
    if (call) {
      setActiveCall({ ...call });
    }
    return newState;
  }, [callService]);

  const toggleScreenShare = useCallback(async () => {
    if (!callService) {
      return false;
    }
    const newState = await callService.toggleScreenShare();
    const call = callService.getActiveCall();
    if (call) {
      setActiveCall({ ...call });
    }
    return newState;
  }, [callService]);

  return useMemo(
    () => ({
      callService,
      activeCall,
      callSupported,
      callSupportLoading,
      startCall,
      endCall,
      toggleMute,
      toggleDeafen,
      toggleVideo,
      toggleScreenShare,
    }),
    [callService, activeCall, callSupported, callSupportLoading, startCall, endCall, toggleMute, toggleDeafen, toggleVideo, toggleScreenShare]
  );
}

/**
 * Hook to check if a call is active in a specific room
 */
export function useRoomCall(roomId: string) {
  const { activeCall, startCall, endCall, callSupported: globalCallSupported, callSupportLoading: globalCallSupportLoading, callService } = useCall();
  const mx = useMatrixClient();
  const [roomCallSupported, setRoomCallSupported] = useState(false);
  const [roomCallSupportLoading, setRoomCallSupportLoading] = useState(true);

  // Check if calls are supported for this specific room by checking all priority homeservers
  useEffect(() => {
    const checkRoomCallSupport = async () => {
      setRoomCallSupportLoading(true);
      
      const room = mx.getRoom(roomId);
      if (!room) {
        setRoomCallSupported(false);
        setRoomCallSupportLoading(false);
        return;
      }

      // Get homeserver priority list for this room
      const homeserverPriority = getLiveKitHomeserverPriority(
        room,
        mx.getHomeserverUrl(),
        mx.getSafeUserId()
      );

      console.log(`Checking call support for room ${roomId}, homeserver priority:`, homeserverPriority);

      // Check each homeserver in priority order
      for (const homeserver of homeserverPriority) {
        try {
          console.log(`Fetching well-known from ${homeserver}...`);
          const wellKnown = await fetchWellKnownWithRTC(homeserver);
          console.log(`Well-known response from ${homeserver}:`, wellKnown);
          
          const livekitFocus = getLiveKitFocus(wellKnown);
          console.log(`LiveKit focus from ${homeserver}:`, livekitFocus);
          
          if (livekitFocus) {
            console.log(`Call support found for room ${roomId} on homeserver: ${homeserver}`);
            setRoomCallSupported(true);
            setRoomCallSupportLoading(false);
            return;
          }
        } catch (error) {
          console.warn(`Failed to check call support on ${homeserver}:`, error);
          // Continue to next homeserver
        }
      }

      // No homeserver supports calls
      console.log(`No call support found for room ${roomId}`);
      setRoomCallSupported(false);
      setRoomCallSupportLoading(false);
    };

    checkRoomCallSupport();
  }, [roomId, mx]);

  const isInCall = activeCall?.roomId === roomId;
  const callState = isInCall ? activeCall.state : CallState.Idle;
  const isUsingRemoteHomeserver = isInCall ? callService?.isUsingRemoteHomeserver() ?? false : false;

  // Check if a remote homeserver WOULD be used (pre-call check)
  const wouldUseRemoteHomeserver = useMemo(() => {
    if (isInCall) return isUsingRemoteHomeserver;
    
    const room = mx.getRoom(roomId);
    if (!room) return false;
    
    const homeserverPriority = getLiveKitHomeserverPriority(
      room,
      mx.getHomeserverUrl(),
      mx.getSafeUserId()
    );
    
    // Check if first priority is different from user's homeserver
    const userHomeserverDomain = mx.getHomeserverUrl()
      .replace('https://', '')
      .replace('http://', '');
    const firstPriorityDomain = homeserverPriority[0]
      ?.replace('https://', '')
      .replace('http://', '');
    
    return firstPriorityDomain !== userHomeserverDomain;
  }, [isInCall, isUsingRemoteHomeserver, mx, roomId]);

  const startVoiceCall = useCallback(() => startCall(roomId, CallType.Voice), [roomId, startCall]);
  const startVideoCall = useCallback(() => startCall(roomId, CallType.Video), [roomId, startCall]);

  return {
    isInCall,
    callState,
    activeCall: isInCall ? activeCall : null,
    isUsingRemoteHomeserver: wouldUseRemoteHomeserver,
    startVoiceCall,
    startVideoCall,
    endCall,
    callSupported: roomCallSupported,
    callSupportLoading: roomCallSupportLoading,
  };
}
