import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useJamServerContext } from '../context/JamServerContext';
import {
  addParticipant,
  removeParticipant,
  updateParticipant,
  addUserTrack,
  removeUserTrack,
  updateUserTrack,
  addJamTrack,
  removeJamTrack,
  addBackingTrack,
  removeBackingTrack,
  startRecording,
  stopRecording,
  addRecordedTrack,
  setConnectionStatus,
  setAvailableMixdowns
} from '../store/features/activeSessionSlice';
import {
  updateMediaSummary,
  setMetronome
} from '../store/features/mixersSlice';
import {
  setBackingTracks,
  setJamTracks,
  setRecordedTracks,
  updateJamTrackState,
  setDownloadState
} from '../store/features/mediaSlice';
import {
  addMessageFromWebSocket,
  incrementUnreadCount
} from '../store/features/sessionChatSlice';

/**
 * Helper function to get channel key from WebSocket chat message
 *
 * Channel keys map Protocol Buffer channel types to Redux state keys:
 * - Session messages: Use session_id directly
 * - Lesson messages: Use lesson_session_id directly
 * - Global messages: Use 'global' literal
 *
 * @param {Object} message - WebSocket message with channel and ID fields
 * @param {string} message.channel - Channel type ('session', 'lesson', or 'global')
 * @param {string} [message.session_id] - Session ID (for session channel)
 * @param {string} [message.lesson_session_id] - Lesson session ID (for lesson channel)
 * @returns {string} Channel key for Redux state
 */
const getChannelKeyFromMessage = (message) => {
  if (message.channel === 'session' && message.session_id) {
    return message.session_id;
  }
  if (message.channel === 'lesson' && message.lesson_session_id) {
    return message.lesson_session_id;
  }
  return 'global';
};

/**
 * Custom hook to integrate WebSocket messages with Redux state
 * Listens to WebSocket events and dispatches appropriate Redux actions
 *
 * @param {string} sessionId - The active session ID
 */
export const useSessionWebSocket = (sessionId) => {
  const dispatch = useDispatch();
  const { jamServer, isConnected } = useJamServerContext();

  // Get chat state for unread increment logic
  const chatState = useSelector((state) => state.sessionChat || { isWindowOpen: false, activeChannel: null });

  useEffect(() => {
    if (!jamServer || !sessionId) return;

    // Update connection status
    dispatch(setConnectionStatus(isConnected ? 'connected' : 'disconnected'));

    // Define message callbacks that dispatch Redux actions
    const callbacks = {
      // Participant events
      participantJoined: (data) => {
        console.log('Participant joined:', data);
        dispatch(addParticipant(data.participant));
      },

      participantLeft: (data) => {
        console.log('Participant left:', data);
        dispatch(removeParticipant(data.participantId));
      },

      participantUpdated: (data) => {
        console.log('Participant updated:', data);
        dispatch(updateParticipant(data.participant));
      },

      // Track events
      trackAdded: (data) => {
        console.log('Track added:', data);
        dispatch(addUserTrack(data.track));
      },

      trackRemoved: (data) => {
        console.log('Track removed:', data);
        dispatch(removeUserTrack(data.trackId));
      },

      trackUpdated: (data) => {
        console.log('Track updated:', data);
        dispatch(updateUserTrack(data.track));
      },

      // Jam Track events
      jamTrackAdded: (data) => {
        console.log('Jam track added:', data);
        dispatch(addJamTrack(data.jamTrack));
      },

      jamTrackRemoved: (data) => {
        console.log('Jam track removed:', data);
        dispatch(removeJamTrack(data.jamTrackId));
      },

      // Backing Track events
      backingTrackAdded: (data) => {
        console.log('Backing track added:', data);
        dispatch(addBackingTrack(data.backingTrack));
      },

      backingTrackRemoved: (data) => {
        console.log('Backing track removed:', data);
        dispatch(removeBackingTrack(data.backingTrackId));
      },

      // Recording events
      recordingStarted: (data) => {
        console.log('Recording started:', data);
        dispatch(startRecording(data.recordingId));
      },

      recordingStopped: (data) => {
        console.log('Recording stopped:', data);
        dispatch(stopRecording());
      },

      recordedTrackReady: (data) => {
        console.log('Recorded track ready:', data);
        dispatch(addRecordedTrack(data.track));
      },

      // Phase 5: Mixer and Media events
      // NOTE: MIXER_CHANGES message type not currently sent by server
      // Media arrays (backingTracks, jamTracks, recordedTracks) are now extracted
      // from session REST API response in useSessionModel.js
      MIXER_CHANGES: (sessionMixers) => {
        console.log('[MIXER_CHANGES] Received (legacy handler):', sessionMixers);
        const session = sessionMixers.session;
        const mixers = sessionMixers.mixers;

        // Update media summary
        if (mixers.mediaSummary) {
          dispatch(updateMediaSummary(mixers.mediaSummary));
        }

        // Update media arrays
        dispatch(setBackingTracks(mixers.backingTracks || []));
        dispatch(setJamTracks(mixers.jamTracks || []));
        dispatch(setRecordedTracks(mixers.recordedTracks || []));
        dispatch(setMetronome(mixers.metronome || null));

        // Phase 5 Plan 2: Handle JamTrack mixdown changes
        if (mixers.jamTrackMixdowns) {
          dispatch(setAvailableMixdowns(mixers.jamTrackMixdowns));
        }
      },

      JAM_TRACK_CHANGES: (changes) => {
        console.log('Jam track changes received:', changes);
        dispatch(updateJamTrackState({
          isPlaying: changes.jamTrackState?.isPlaying || false,
          isPaused: changes.jamTrackState?.isPaused || false,
          currentPositionMs: parseInt(changes.jamTrackState?.currentPositionMs || '0', 10),
          lastUpdate: Date.now()
        }));
      },

      MIXDOWN_CHANGES: (message) => {
        console.log('Mixdown changes received:', message);
        if (message.mixdownPackage) {
          dispatch(setDownloadState({
            currentStep: message.mixdownPackage.current_packaging_step || 0,
            totalSteps: message.mixdownPackage.packaging_steps || 0
          }));
        }
      },

      /**
       * Phase 7 Plan 3: Handle CHAT_MESSAGE from WebSocket
       *
       * Transforms Protocol Buffer message format to Redux format and handles
       * unread count increment based on chat window state.
       *
       * Unread increment logic:
       * - Increment if window is closed
       * - Increment if window is open but viewing a different channel
       * - Do NOT increment if window is open and viewing the same channel
       *
       * @param {Object} message - Protocol Buffer formatted chat message
       */
      CHAT_MESSAGE: (message) => {
        console.log('Chat message received:', message);

        // Transform Protocol Buffer format to Redux format
        const chatMessage = {
          id: message.msg_id,
          senderId: message.user_id,
          senderName: message.user_name,
          message: message.message,
          channel: message.channel,
          sessionId: message.session_id || null,
          lessonSessionId: message.lesson_session_id || null,
          createdAt: message.created_at,
          purpose: message.purpose || null,
          musicNotation: message.music_notation || null,
          claimedRecording: message.claimed_recording || null
        };

        // Add message to Redux state
        dispatch(addMessageFromWebSocket(chatMessage));

        // Increment unread count if window closed or viewing different channel
        const messageChannelKey = getChannelKeyFromMessage(message);
        const shouldIncrementUnread = !chatState.isWindowOpen || chatState.activeChannel !== messageChannelKey;
        if (shouldIncrementUnread) {
          dispatch(incrementUnreadCount({ channel: messageChannelKey }));
        }
      },

      // Handle WebSocket subscription notifications (e.g., mixdown packaging progress)
      SUBSCRIPTION_MESSAGE: (header, payload) => {
        console.log('[WebSocket] Subscription message received:', { header, payload });

        // Parse the payload body (may be JSON string)
        let body;
        try {
          body = typeof payload.body === 'string' ? JSON.parse(payload.body) : payload.body;
        } catch (err) {
          console.error('[WebSocket] Failed to parse subscription message body:', err);
          return;
        }

        // Handle mixdown packaging progress
        if (payload.type === 'mixdown' && body) {
          dispatch(setDownloadState({
            signing_state: body.signing_state,
            packaging_steps: body.packaging_steps || 0,
            current_packaging_step: body.current_packaging_step || 0
          }));

          console.log(`[WebSocket] Mixdown packaging: ${body.signing_state}, step ${body.current_packaging_step}/${body.packaging_steps}`);

          // If packaging failed, set error state
          if (body.signing_state === 'ERROR' ||
              body.signing_state === 'SIGNING_TIMEOUT' ||
              body.signing_state === 'QUEUED_TIMEOUT' ||
              body.signing_state === 'QUIET_TIMEOUT') {
            dispatch(setDownloadState({
              state: 'error',
              error: {
                type: 'download',
                message: `Packaging failed: ${body.signing_state}`
              }
            }));
          }
        }
      },

      // Connection events
      connectionStatusChanged: (data) => {
        console.log('Connection status changed:', data);
        dispatch(setConnectionStatus(data.status));
      }
    };

    // Register callbacks with jamServer
    // Note: The actual event names will depend on your WebSocket implementation
    // Adjust these based on your Protocol Buffer message types
    Object.entries(callbacks).forEach(([event, callback]) => {
      if (jamServer.on) {
        jamServer.on(event, callback);
      } else if (jamServer.registerMessageCallback) {
        // If using the registerMessageCallback pattern from the legacy code
        jamServer.registerMessageCallback(event, callback);
      }
    });

    // Cleanup function to unregister callbacks
    return () => {
      Object.entries(callbacks).forEach(([event, callback]) => {
        if (jamServer.off) {
          jamServer.off(event, callback);
        } else if (jamServer.unregisterMessageCallback) {
          jamServer.unregisterMessageCallback(event, callback);
        }
      });
    };
  }, [jamServer, sessionId, isConnected, dispatch, chatState]);
};
