import { useCallback, useEffect, useRef, useState } from 'react';
import useWebSocket, { ReadyState } from 'react-use-websocket';

import { devError, devWarn } from '../utils/debug';
import { NETWORK_CONFIG } from '../config/constants';

// Audio event types matching the backend
export type AudioEventType = 
  | 'audio-mute-changed'
  | 'microphone-state-changed'
  | 'audio-device-changed';

// Audio event data interfaces
export interface AudioMuteData {
  muted: boolean;
}

export interface MicrophoneStateData {
  running: boolean;
  session_active: boolean;
}

export interface AudioDeviceChangedData {
  enabled: boolean;
  reason: string;
}

// Audio event structure
export interface AudioEvent {
  type: AudioEventType;
  data: AudioMuteData | MicrophoneStateData | AudioDeviceChangedData;
}

// Hook return type
export interface UseAudioEventsReturn {
  // Connection state
  connectionState: ReadyState;
  isConnected: boolean;
  
  // Audio state
  audioMuted: boolean | null;
  
  // Microphone state
  microphoneState: MicrophoneStateData | null;
  
  // Device change events
  onAudioDeviceChanged?: (data: AudioDeviceChangedData) => void;
  
  // Manual subscription control
  subscribe: () => void;
  unsubscribe: () => void;
}

// Global subscription management to prevent multiple subscriptions per WebSocket connection
const globalSubscriptionState = {
  isSubscribed: false,
  subscriberCount: 0,
  connectionId: null as string | null
};

export function useAudioEvents(onAudioDeviceChanged?: (data: AudioDeviceChangedData) => void): UseAudioEventsReturn {
  // State for audio data
  const [audioMuted, setAudioMuted] = useState<boolean | null>(null);
  const [microphoneState, setMicrophoneState] = useState<MicrophoneStateData | null>(null);
  
  // Local subscription state
  const [isLocallySubscribed, setIsLocallySubscribed] = useState(false);
  const subscriptionTimeoutRef = useRef<number | null>(null);

  // Get WebSocket URL
  const getWebSocketUrl = () => {
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const host = window.location.host;
    return `${protocol}//${host}/webrtc/signaling/client`;
  };

  // Shared WebSocket connection using the `share` option for better resource management
  const {
    sendMessage,
    lastMessage,
    readyState,
  } = useWebSocket(getWebSocketUrl(), {
    shouldReconnect: () => true,
    reconnectAttempts: 10,
    reconnectInterval: NETWORK_CONFIG.WEBSOCKET_RECONNECT_INTERVAL,
    share: true, // Share the WebSocket connection across multiple hooks
    onOpen: () => {
      // WebSocket connected
      // Reset global state on new connection
      globalSubscriptionState.isSubscribed = false;
      globalSubscriptionState.connectionId = Math.random().toString(36);
    },
    onClose: () => {
      // WebSocket disconnected
      // Reset global state on disconnect
      globalSubscriptionState.isSubscribed = false;
      globalSubscriptionState.subscriberCount = 0;
      globalSubscriptionState.connectionId = null;
    },
    onError: (event) => {
      devError('[AudioEvents] WebSocket error:', event);
    },
  });

  // Subscribe to audio events
  const subscribe = useCallback(() => {
    if (readyState === ReadyState.OPEN && !globalSubscriptionState.isSubscribed) {
      // Clear any pending subscription timeout
      if (subscriptionTimeoutRef.current) {
        clearTimeout(subscriptionTimeoutRef.current);
        subscriptionTimeoutRef.current = null;
      }

      // Add a small delay to prevent rapid subscription attempts
      subscriptionTimeoutRef.current = setTimeout(() => {
        if (readyState === ReadyState.OPEN && !globalSubscriptionState.isSubscribed) {
          const subscribeMessage = {
            type: 'subscribe-audio-events',
            data: {}
          };
          
          sendMessage(JSON.stringify(subscribeMessage));
          globalSubscriptionState.isSubscribed = true;
          // Subscribed to audio events
        }
      }, 100); // 100ms delay to debounce subscription attempts
    }
    
    // Track local subscription regardless of global state
    if (!isLocallySubscribed) {
      globalSubscriptionState.subscriberCount++;
      setIsLocallySubscribed(true);
    }
  }, [readyState, sendMessage, isLocallySubscribed]);

  // Unsubscribe from audio events
  const unsubscribe = useCallback(() => {
    // Clear any pending subscription timeout
    if (subscriptionTimeoutRef.current) {
      clearTimeout(subscriptionTimeoutRef.current);
      subscriptionTimeoutRef.current = null;
    }

    if (isLocallySubscribed) {
      globalSubscriptionState.subscriberCount--;
      setIsLocallySubscribed(false);
      
      // Only send unsubscribe message if this is the last subscriber and connection is still open
      if (globalSubscriptionState.subscriberCount <= 0 && 
          readyState === ReadyState.OPEN && 
          globalSubscriptionState.isSubscribed) {
        
        const unsubscribeMessage = {
          type: 'unsubscribe-audio-events',
          data: {}
        };
        
        sendMessage(JSON.stringify(unsubscribeMessage));
        globalSubscriptionState.isSubscribed = false;
        globalSubscriptionState.subscriberCount = 0;
        // Sent unsubscribe message to backend
      }
    }
    
    // Component unsubscribed from audio events
  }, [readyState, isLocallySubscribed, sendMessage]);

  // Handle incoming messages
  useEffect(() => {
    if (lastMessage !== null) {
      try {
        const message = JSON.parse(lastMessage.data);
        
        // Handle audio events
        if (message.type && message.data) {
          const audioEvent = message as AudioEvent;
          
          switch (audioEvent.type) {
            case 'audio-mute-changed': {
              const muteData = audioEvent.data as AudioMuteData;
              setAudioMuted(muteData.muted);
              // Audio mute changed
              break;
            }
              
            case 'microphone-state-changed': {
              const micStateData = audioEvent.data as MicrophoneStateData;
              setMicrophoneState(micStateData);
              // Microphone state changed
              break;
            }
              
            case 'audio-device-changed': {
              const deviceChangedData = audioEvent.data as AudioDeviceChangedData;
              // Audio device changed
              if (onAudioDeviceChanged) {
                onAudioDeviceChanged(deviceChangedData);
              }
              break;
            }
              
            default:
              // Ignore other message types (WebRTC signaling, etc.)
              break;
          }
        }
      } catch (error) {
        // Ignore parsing errors for non-JSON messages (like "pong")
        if (lastMessage.data !== 'pong') {
          devWarn('[AudioEvents] Failed to parse WebSocket message:', error);
        }
      }
    }
  }, [lastMessage, onAudioDeviceChanged]);

  // Auto-subscribe when connected
  useEffect(() => {
    if (readyState === ReadyState.OPEN) {
      subscribe();
    }
    
    // Cleanup subscription on component unmount or connection change
    return () => {
      if (subscriptionTimeoutRef.current) {
        clearTimeout(subscriptionTimeoutRef.current);
        subscriptionTimeoutRef.current = null;
      }
      unsubscribe();
    };
  }, [readyState, subscribe, unsubscribe]);

  // Reset local subscription state on disconnect
  useEffect(() => {
    if (readyState === ReadyState.CLOSED || readyState === ReadyState.CLOSING) {
      setIsLocallySubscribed(false);
      if (subscriptionTimeoutRef.current) {
        clearTimeout(subscriptionTimeoutRef.current);
        subscriptionTimeoutRef.current = null;
      }
    }
  }, [readyState]);

  // Cleanup on component unmount
  useEffect(() => {
    return () => {
      unsubscribe();
    };
  }, [unsubscribe]);

  return {
    // Connection state
    connectionState: readyState,
    isConnected: readyState === ReadyState.OPEN && globalSubscriptionState.isSubscribed,
    
    // Audio state
    audioMuted,
    
    // Microphone state
    microphoneState,
    
    // Device change events
    onAudioDeviceChanged,
    
    // Manual subscription control
    subscribe,
    unsubscribe,
  };
}