import { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
  openBackingTrack as openBackingTrackThunk,
  loadJamTrack as loadJamTrackThunk,
  closeMedia as closeMediaThunk,
  clearJamTrackState,
  updateJamTrackState,
  setBackingTracks
} from '../store/features/mediaSlice';
import {
  setMetronome,
  setMetronomeSettings,
  updateMediaSummary
} from '../store/features/mixersSlice';
import {
  setShowMyMixes,
  toggleMyMixes as toggleMyMixesAction,
  setShowCustomMixes,
  toggleCustomMixes as toggleCustomMixesAction,
  setEditingMixdownId,
  setCreatingMixdown,
  setCreateMixdownErrors
} from '../store/features/sessionUISlice';
import {
  selectSessionId,
  addBackingTrack as addBackingTrackToActiveSession
} from '../store/features/activeSessionSlice';
import { useJamServerContext } from '../context/JamServerContext';
import { syncTracksToServer } from '../services/trackSyncService';

/**
 * Custom hook that provides Redux-based media actions
 * Replaces MediaContext actions with Redux thunks and dispatchers
 */
const useMediaActions = () => {
  const dispatch = useDispatch();
  const sessionId = useSelector(selectSessionId);
  const { jamClient, subscribe, unsubscribe, server } = useJamServerContext();

  // Create jamServer object with subscribe/unsubscribe for thunks
  const jamServer = useMemo(() => ({
    subscribe,
    unsubscribe
  }), [subscribe, unsubscribe]);

  /**
   * Open a backing track file
   * @param {string} file - Path to the backing track file
   */
  const openBackingTrack = useCallback(async (file) => {
    try {
      const result = await dispatch(openBackingTrackThunk({ file, jamClient })).unwrap();

      // Update backing tracks in both Redux slices
      // 1. activeSession slice (for syncTracksToServer)
      // 2. media slice (for session screen UI)
      if (result.backingTracks && result.backingTracks.length > 0) {
        console.log('[openBackingTrack] Updating Redux with backing tracks:', result.backingTracks);

        // Add each backing track to activeSession (for track sync)
        for (const bt of result.backingTracks) {
          dispatch(addBackingTrackToActiveSession(bt));
        }

        // Set backing tracks in media slice (for UI display)
        // Format for UI: { shortFilename, filename, mixers (will be added by mixer system) }
        dispatch(setBackingTracks(result.backingTracks));
      }

      // Update media summary
      dispatch(updateMediaSummary({
        backingTrackOpen: true,
        userNeedsMediaControls: true
      }));

      // Sync tracks to server after opening backing track
      if (sessionId && server?.clientId) {
        console.log('[Track Sync] Backing track opened, syncing tracks');
        dispatch(syncTracksToServer(sessionId, server.clientId));
      }
    } catch (error) {
      console.error('Error opening backing track:', error);
      throw error;
    }
  }, [dispatch, jamClient, sessionId, server]);

  /**
   * Close all media (backing tracks, jam tracks, recordings, metronome)
   * @param {boolean} force - Force close even if media is playing
   */
  const closeMedia = useCallback(async (force = false) => {
    try {
      await dispatch(closeMediaThunk({ force, jamClient })).unwrap();

      // Update media summary
      dispatch(updateMediaSummary({
        mediaOpen: false,
        backingTrackOpen: false,
        jamTrackOpen: false,
        recordingOpen: false,
        metronomeOpen: false,
        userNeedsMediaControls: false
      }));
    } catch (error) {
      console.error('Error closing media:', error);
      throw error;
    }
  }, [dispatch, jamClient]);

  /**
   * Open metronome with specified settings
   * @param {number} bpm - Beats per minute (default: 120)
   * @param {string} sound - Metronome sound type (default: "Beep")
   * @param {number} meter - Time signature meter (default: 1)
   * @param {number} mode - Metronome mode (default: 0)
   */
  const openMetronome = useCallback(async (bpm = 120, sound = "Beep", meter = 1, mode = 0) => {
    try {
      const result = await jamClient.SessionOpenMetronome(bpm, sound, meter, mode);

      // Update Redux state
      dispatch(setMetronome({ bpm, sound, meter, mode }));
      dispatch(setMetronomeSettings({ tempo: bpm, sound, cricket: mode === 1 }));
      dispatch(updateMediaSummary({
        metronomeOpen: true,
        userNeedsMediaControls: true
      }));

      // Sync tracks to server after opening metronome
      if (sessionId && server?.clientId) {
        console.log('[Track Sync] Metronome opened, syncing tracks');
        dispatch(syncTracksToServer(sessionId, server.clientId));
      }

      return result;
    } catch (error) {
      console.error('Error opening metronome:', error);
      throw error;
    }
  }, [dispatch, jamClient, sessionId, server]);

  /**
   * Close the metronome
   */
  const closeMetronome = useCallback(async () => {
    try {
      await jamClient.SessionCloseMetronome();

      // Update Redux state
      dispatch(setMetronome(null));
      dispatch(updateMediaSummary({
        metronomeOpen: false
      }));

      // Sync tracks to server after closing metronome
      if (sessionId && server?.clientId) {
        console.log('[Track Sync] Metronome closed, syncing tracks');
        dispatch(syncTracksToServer(sessionId, server.clientId));
      }
    } catch (error) {
      console.error('Error closing metronome:', error);
      throw error;
    }
  }, [dispatch, jamClient, sessionId, server]);

  /**
   * Load and play a JamTrack
   * @param {object} jamTrack - JamTrack object with id and optional jmep data
   */
  const loadJamTrack = useCallback(async (jamTrack) => {
    try {
      await dispatch(loadJamTrackThunk({ jamTrack, jamClient, jamServer })).unwrap();

      // Update media summary
      dispatch(updateMediaSummary({
        jamTrackOpen: true,
        userNeedsMediaControls: true
      }));

      // Sync tracks to server after opening jam track
      if (sessionId && server?.clientId) {
        console.log('[Track Sync] Jam track opened, syncing tracks');
        dispatch(syncTracksToServer(sessionId, server.clientId));
      }
    } catch (error) {
      console.error('Error loading jam track:', error);
      throw error;
    }
  }, [dispatch, jamClient, jamServer, sessionId, server]);

  /**
   * Stop and close the currently playing JamTrack
   */
  const closeJamTrack = useCallback(async () => {
    try {
      await jamClient.JamTrackStopPlay();

      // Update Redux state
      dispatch(clearJamTrackState());
      dispatch(updateMediaSummary({
        jamTrackOpen: false
      }));
    } catch (error) {
      console.error('Error closing jam track:', error);
      throw error;
    }
  }, [dispatch, jamClient]);

  /**
   * Update JamTrack playback state (position, playing, etc.)
   * @param {object} changes - JamTrack state changes
   */
  const updateJamTrackPlayback = useCallback((changes) => {
    dispatch(updateJamTrackState(changes));
  }, [dispatch]);

  // UI Actions
  const toggleMyMixes = useCallback(() => {
    dispatch(toggleMyMixesAction());
  }, [dispatch]);

  const toggleCustomMixes = useCallback(() => {
    dispatch(toggleCustomMixesAction());
  }, [dispatch]);

  const editMixdown = useCallback((mixdownId) => {
    dispatch(setEditingMixdownId(mixdownId));
  }, [dispatch]);

  const startCreatingMixdown = useCallback(() => {
    dispatch(setCreatingMixdown(true));
  }, [dispatch]);

  const stopCreatingMixdown = useCallback(() => {
    dispatch(setCreatingMixdown(false));
  }, [dispatch]);

  const setMixdownErrors = useCallback((errors) => {
    dispatch(setCreateMixdownErrors(errors));
  }, [dispatch]);

  return {
    // Core media actions
    openBackingTrack,
    closeMedia,
    openMetronome,
    closeMetronome,
    loadJamTrack,
    closeJamTrack,
    updateJamTrackPlayback,

    // UI actions
    toggleMyMixes,
    toggleCustomMixes,
    editMixdown,
    startCreatingMixdown,
    stopCreatingMixdown,
    setMixdownErrors
  };
};

export default useMediaActions;
