import { useState, useCallback, useRef, useEffect, useContext } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { selectSessionId } from '../store/features/activeSessionSlice';
import { setRecordingStarted, setRecordingStopped, selectThisClientStartedRecording, selectRecordingId } from '../store/features/sessionUISlice';
import { useJamKazamApp } from '../context/JamKazamAppContext';
import { startRecording as startRecordingRest, stopRecording as stopRecordingRest, getRecordingPromise } from '../helpers/rest';
import { useGlobalContext } from '../context/GlobalContext';
import { RECORD_TYPE_BOTH } from '../helpers/globals'

const useRecordingHelpers = (jamClient) => {
  const app = useJamKazamApp();
  const dispatch = useDispatch();
  // Phase 4: Replace CurrentSessionContext with Redux
  const sessionId = useSelector(selectSessionId);

  // State variables from original RecordingModel
  const [currentRecording, setCurrentRecording] = useState(null);
  const [currentOrLastRecordingId, setCurrentOrLastRecordingId] = useState(null);
  const [currentRecordingId, setCurrentRecordingId] = useState(null);
  // thisClientStartedRecording now comes from Redux to share state across hook instances
  const thisClientStartedRecording = useSelector(selectThisClientStartedRecording);
  const reduxRecordingId = useSelector(selectRecordingId);
  const [currentlyRecording, setCurrentlyRecording] = useState(false);
  const [startingRecording, setStartingRecording] = useState(false);
  const [stoppingRecording, setStoppingRecording] = useState(false);
  const [waitingOnServerStop, setWaitingOnServerStop] = useState(false);
  const [waitingOnClientStop, setWaitingOnClientStop] = useState(false);

  const { globalObject, setGlobalObject } = useGlobalContext();

  const waitingOnStopTimer = useRef(null);

  // Cleanup timer on unmount
  useEffect(() => {
    return () => {
      if (waitingOnStopTimer.current) {
        clearTimeout(waitingOnStopTimer.current);
        waitingOnStopTimer.current = null;
      }
    };
  }, []);

  // Get state function
  const getState = useCallback(() => ({
    waitingOnClientStop,
    waitingOnServerStop,
    stoppingRecording,
    startingRecording
  }), [waitingOnClientStop, waitingOnServerStop, stoppingRecording, startingRecording]);

  // Check if recording
  const isRecording = useCallback((recordingId) => {
    if (recordingId) {
      return recordingId === currentRecordingId;
    } else {
      return currentlyRecording;
    }
  }, [currentRecordingId, currentlyRecording]);

  // Get this client started recording
  const getThisClientStartedRecording = useCallback(() => thisClientStartedRecording, [thisClientStartedRecording]);

  // Reset function
  const reset = useCallback((sessionId) => {
    console.log("[RecordingState]: reset");
    setCurrentlyRecording(false);
    setWaitingOnServerStop(false);
    setWaitingOnClientStop(false);
    if (waitingOnStopTimer.current) {
      clearTimeout(waitingOnStopTimer.current);
      waitingOnStopTimer.current = null;
    }
    setCurrentRecording(null);
    setCurrentRecordingId(null);
    setStoppingRecording(false);
    // thisClientStartedRecording is reset via setRecordingStopped Redux action
    dispatch(setRecordingStopped());
  }, [dispatch]);

  // Group tracks to client
  const groupTracksToClient = useCallback((recording) => {
    if (!recording) {
      return [];
    }
    const groupedTracks = {};
    const recordingTracks = recording.recorded_tracks || [];
    for (let i = 0; i < recordingTracks.length; i++) {
      const clientId = recordingTracks[i].client_id;
      let tracksForClient = groupedTracks[clientId];
      if (!tracksForClient) {
        tracksForClient = [];
        groupedTracks[clientId] = tracksForClient;
      }
      tracksForClient.push(recordingTracks[i]);
    }
    return Object.keys(groupedTracks);
  }, []);

  // Start recording
  const startRecording = useCallback(async (recordSettings) => {
    const recordVideo = recordSettings.recordingType === RECORD_TYPE_BOTH;

    // Trigger startingRecording event
    // In React, we might use a callback or context to notify parent components

    setCurrentlyRecording(true);
    setStoppingRecording(false);
    // Note: thisClientStartedRecording is now set via Redux in handleRecordingStartResult

    // Context RecordingActions.startingRecording would be handled by parent or context

    try {
      console.log(`[RecordingState] Starting recording for session=${sessionId}`);
      const recording = await startRecordingRest({ music_session_id: sessionId, record_video: recordVideo });
      console.log(`[RecordingState] startRecordingRest succeeded:`, JSON.stringify(recording, null, 2));
      setCurrentRecording(recording);
      setCurrentRecordingId(recording.id);
      setCurrentOrLastRecordingId(recording.id);

      const groupedTracks = groupTracksToClient(recording);
      console.log("jamClient#StartMediaRecording", recording.id, groupedTracks, recordSettings);
      await jamClient.StartMediaRecording(recording.id, groupedTracks, recordSettings);
      return recording;
    } catch (error) {
      // apiFetch rejects with the Response object on non-2xx status
      let errorDetail = error;
      if (error && typeof error.json === 'function') {
        try {
          errorDetail = await error.json();
          console.error("[RecordingState] startRecording failed with status", error.status, "details:", JSON.stringify(errorDetail, null, 2));
        } catch (e) {
          console.error("[RecordingState] startRecording failed with status", error.status, "could not parse response");
        }
      } else {
        console.error("[RecordingState] startRecording failed:", error);
      }
      const details = { clientId: app.clientId, reason: 'rest', detail: errorDetail, isRecording: false };
      // Trigger startedRecording event
      setCurrentlyRecording(false);
      // Context RecordingActions.startedRecording(details);
    }

    return true;
  }, [sessionId, groupTracksToClient, jamClient, app.clientId]);

  // Transition to stopped
  const transitionToStopped = useCallback(() => {
    console.log("[RecordingState] transitionToStopped");
    setCurrentlyRecording(false);
    setCurrentRecording(null);
    setCurrentRecordingId(null);
    // Update Redux state
    dispatch(setRecordingStopped());
    if (waitingOnStopTimer.current) {
      clearTimeout(waitingOnStopTimer.current);
      waitingOnStopTimer.current = null;
    }
  }, [dispatch]);

  // Attempt transition to stop
  const attemptTransitionToStop = useCallback((recordingId, errorReason, errorDetail) => {
    if (!waitingOnClientStop && !waitingOnServerStop) {
      transitionToStopped();
      const details = { recordingId, reason: errorReason, detail: errorDetail, isRecording: false };
      // Trigger stoppedRecording event
      // Context RecordingActions.stoppedRecording(details)
    }
  }, [waitingOnClientStop, waitingOnServerStop, transitionToStopped]);

  // Timeout transition to stop
  const timeoutTransitionToStop = useCallback(() => {
    waitingOnStopTimer.current = null;
    transitionToStopped();
    // Trigger stoppedRecordingFailed event
  }, [transitionToStopped]);

  // Stop recording
  const stopRecording = useCallback(async (recordingId, reason, detail) => {
    const userInitiated = recordingId == null && reason == null && detail == null;

    // CRITICAL: Capture thisClientStartedRecording at the START before any async operations.
    // Another hook instance may dispatch setRecordingStopped() via handleRecordingStopResult
    // callback which would set thisClientStartedRecording=false in Redux before we check it.
    const shouldCallRestStop = thisClientStartedRecording;

    console.log(`[RecordingState]: stopRecording userInitiated=${userInitiated} thisClientStartedRecording=${thisClientStartedRecording} shouldCallRestStop=${shouldCallRestStop} reason=${reason} detail=${detail}`);

    if (stoppingRecording) {
      console.log("ignoring stopRecording because we are already stopping");
      return;
    }
    setStoppingRecording(true);

    setWaitingOnServerStop(true);
    setWaitingOnClientStop(true);
    waitingOnStopTimer.current = setTimeout(timeoutTransitionToStop, 5000);

    // Trigger stoppingRecording event
    // Context RecordingActions.stoppingRecording

    try {
      const recording = currentRecording;
      // Use Redux recordingId as fallback when local state isn't available (different hook instance)
      const recId = recordingId || currentRecordingId || reduxRecordingId;

      // Get grouped tracks if we have the recording object, otherwise use empty array
      const groupedTracks = recording ? groupTracksToClient(recording) : [];

      await jamClient.FrontStopRecording(recId, groupedTracks);

      // Use the captured value, not the current Redux value which may have changed
      if (shouldCallRestStop) {
        try {
          console.log(`[RecordingState] Calling stopRecordingRest with id=${recId}`);
          const stopResult = await stopRecordingRest({ id: recId });
          console.log(`[RecordingState] stopRecordingRest completed:`, JSON.stringify(stopResult, null, 2));
          console.log(`[RecordingState] stopRecordingRest - is_done=${stopResult?.is_done}, duration=${stopResult?.duration}`);
          setWaitingOnServerStop(false);
          attemptTransitionToStop(recId, reason, detail);
          setStoppingRecording(false);
        } catch (error) {
          setStoppingRecording(false);
          if (error.status === 422) {
            setWaitingOnServerStop(false);
            attemptTransitionToStop(recId, reason, detail);
          } else {
            console.error("unable to stop recording", error);
            transitionToStopped();
            const details = {
              recordingId: recId,
              reason: 'rest',
              details: error,
              isRecording: false
            };
            // Trigger stoppedRecording event
            // Context RecordingActions.stoppedRecording(details)
            setStoppingRecording(false);
          }
        }
      }
    } catch (error) {
      console.error("Error in stopRecording:", error);
      transitionToStopped();
      setStoppingRecording(false);
    }

    return true;
  }, [currentRecording, currentRecordingId, reduxRecordingId, thisClientStartedRecording, stoppingRecording, groupTracksToClient, jamClient, timeoutTransitionToStop, attemptTransitionToStop, transitionToStopped]);

  // Abort recording
  const abortRecording = useCallback(async (recordingId, errorReason, errorDetail) => {
    await jamClient.AbortRecording(recordingId, { reason: errorReason, detail: errorDetail, success: false });
  }, [jamClient]);

  // Handle recording start result - called when native client confirms recording start
  // This is the authoritative confirmation that recording has actually started
  const handleRecordingStartResult = useCallback((recordingId, result) => {
    console.log("[RecordingState] handleRecordingStartResult", { recordingId, result, currentRecordingId, currentlyRecording });

    const { success, reason, detail } = result;

    if (success) {
      // Recording started successfully - NOW update Redux to show "Recording" on toolbar
      dispatch(setRecordingStarted(recordingId));
      const details = { clientId: app.clientId, isRecording: true };
      // Trigger startedRecording event
      // Context RecordingActions.startedRecording(details)
    } else {
      setCurrentlyRecording(false);
      // Recording failed to start - ensure Redux state is cleared
      dispatch(setRecordingStopped());
      console.error("unable to start the recording", reason, detail);
      const details = { clientId: app.clientId, reason, detail, isRecording: false };
      // Trigger startedRecording event
      // Context RecordingActions.startedRecording(details)
    }
  }, [app.clientId, currentRecordingId, currentlyRecording, dispatch]);

  // Handle recording stop result
  const handleRecordingStopResult = useCallback((recordingId, result) => {
    console.log("[RecordingState] handleRecordingStopResult", result);
    const { success, reason, detail } = result;

    setWaitingOnClientStop(false);

    if (success) {
      attemptTransitionToStop(recordingId, reason, detail);
    } else {
      transitionToStopped();
      console.error("backend unable to stop the recording", reason, detail);
      const details = { recordingId, reason, detail, isRecording: false };
      // Trigger stoppedRecording event
      // Context RecordingActions.stoppedRecording(details)
    }
  }, [attemptTransitionToStop]);

  // Handle recording started
  const handleRecordingStarted = useCallback(async (recordingId, result, clientId) => {
    reset(sessionId);
    // Context RecordingActions.resetRecordingState()

    console.log("[RecordingState] handleRecordingStarted called", { recordingId, result, clientId, currentRecordingId, currentlyRecording, sessionId });

    const { success, reason, detail } = result;

    console.log("[RecordingState] Attempting to fetch recording data from server", { recordingId, sessionId });

    try {
      const recording = await getRecordingPromise({ id: recordingId });
      console.log("[RecordingState] Successfully fetched recording data from server", { recordingId: recording.id, ownerId: recording.owner?.id });

      postServerRecordingFetch(recording);
    } catch (error) {
      console.error("[RecordingState] Failed to fetch recording data", { recordingId, error });
    }
  }, [sessionId, reset]);

  // Post server recording fetch
  const postServerRecordingFetch = useCallback((recording) => {
    if (currentRecordingId == null) {
      setCurrentRecordingId(recording.id);
      setCurrentOrLastRecordingId(recording.id);

      const details = { recordingId: currentRecordingId, isRecording: false };
      // Trigger startingRecording event
      // Context RecordingActions.startingRecording(details)
      setCurrentlyRecording(true);
      // Update Redux state
      dispatch(setRecordingStarted(recording.id));

      const startedDetails = { clientId: app.clientId, recordingId: currentRecordingId, isRecording: true };
      // Trigger startedRecording event
      // Context RecordingActions.startedRecording(startedDetails)
    } else if (currentRecordingId === recording.id) {
      // noop
    } else {
      console.error("[RecordingState] we've missed the stop of previous recording", currentRecordingId, recording.id);
      // Show alert
    }
  }, [currentRecordingId, app.clientId, dispatch]);

  // Handle recording stopped
  const handleRecordingStopped = useCallback(async (recordingId, result) => {
    console.log("[RecordingState] handleRecordingStopped event_id=" + recordingId + " current_id=" + currentRecordingId, result);

    const { success, reason, detail } = result;

    const stoppingDetails = { recordingId, reason, detail, isRecording: true };
    // Trigger stoppingRecording event
    // Context RecordingActions.stoppingRecording(stoppingDetails)

    if (recordingId == null || recordingId === "") {
      console.warn("[RecordingState] handleRecordingStopped called with empty recordingId - skipping REST stop call!");
      transitionToStopped();
      const stoppedDetails = { recordingId, reason, detail, isRecording: false };
      // Context RecordingActions.stoppedRecording(stoppedDetails)
      return;
    }

    try {
      console.log(`[RecordingState] handleRecordingStopped calling stopRecordingRest with id=${recordingId}`);
      const stopResult = await stopRecordingRest({ id: recordingId });
      console.log(`[RecordingState] handleRecordingStopped stopRecordingRest completed:`, JSON.stringify(stopResult, null, 2));
      transitionToStopped();
      const details = { recordingId, reason, detail, isRecording: false };
      // Trigger stoppedRecording event
      // Context RecordingActions.stoppedRecording(details)
    } catch (error) {
      if (error.status === 422) {
        console.log("[RecordingState] handleRecordingStopped - recording already stopped (422)", error);
        transitionToStopped();
        const details = { recordingId, reason, detail, isRecording: false };
        // Trigger stoppedRecording event
        // Context RecordingActions.stoppedRecording(details)
      } else if (error.status === 404) {
        console.log("recording is already deleted", error);
        transitionToStopped();
        const details = { recordingId, reason, detail, isRecording: false };
        // Trigger stoppedRecording event
        // Context RecordingActions.stoppedRecording(details)
      } else {
        transitionToStopped();
        const details = { recordingId, reason: error.message || 'error', detail: error, isRecording: false };
        // Trigger stoppedRecording event
        // Context RecordingActions.stoppedRecording(details)
      }
    }
  }, [currentRecordingId, transitionToStopped]);

  // Handle recording aborted
  const handleRecordingAborted = useCallback(async (recordingId, result) => {
    console.log("[RecordingState] handleRecordingAborted");
    if (recordingId === "video") {
      // Handle video abort
      return;
    }

    const { success, reason, detail } = result;

    setStoppingRecording(false);

    const details = { recordingId, reason, detail, isRecording: false };
    // Trigger abortedRecording event
    // Context RecordingActions.abortedRecording(details)

    try {
      await stopRecordingRest({ id: recordingId });
    } catch (error) {
      console.error("Error stopping recording in abort:", error);
    } finally {
      setCurrentlyRecording(false);
    }
  }, []);

  // Stop recording if needed
  const stopRecordingIfNeeded = useCallback(() => {
    return new Promise((resolve) => {
      if (!currentlyRecording) {
        resolve();
      } else {
        // In React, we might use a state or callback to wait for stoppedRecording
        // For now, assume immediate resolve
        resolve();
      }
    });
  }, [currentlyRecording]);

  // Get current recording state
  const getCurrentRecordingState = useCallback(async () => {
    let recording = null;
    let recordingId = null;

    if (jamClient) {
      try {
        recordingId = await jamClient.GetCurrentRecordingId();
      } catch (error) {
        console.error("Error getting current recording ID:", error);
      }
    }

    const isRecording = recordingId && recordingId !== "";

    if (sessionId && isRecording) {
      try {
        recording = await getRecordingPromise({ id: recordingId });
      } catch (error) {
        if (error.status !== 404) {
          console.error("[RecordingState] Failed to fetch server recording state", { recordingId, error });
        }
      }
    }

    if (!sessionId) {
      console.debug("no session, so no recording");
      return { isRecording: false, serverRecording: null, thisClientStartedRecording: false };
    }

    return {
      isRecording,
      isServerRecording: !!recording,
      thisClientStartedRecording
    };
  }, [sessionId, jamClient, thisClientStartedRecording]);

  // Track our callbacks for conditional cleanup
  const callbacksRef = useRef({});

  // Initialize - register global handlers for native client callbacks
  // Note: Multiple hook instances (JKSessionScreen + JKSessionRecordingModal) share window.JK callbacks.
  // Only delete if we still own the callback - another instance may have registered newer ones.
  useEffect(() => {
    if (window) {
      window.JK = window.JK || {};

      // Store our callback references
      callbacksRef.current = {
        HandleRecordingStartResult: handleRecordingStartResult,
        HandleRecordingStopResult: handleRecordingStopResult,
        HandleRecordingStopped: handleRecordingStopped,
        HandleRecordingStarted: handleRecordingStarted,
        HandleRecordingAborted: handleRecordingAborted,
      };

      // Register callbacks
      Object.assign(window.JK, callbacksRef.current);
    }

    return () => {
      // Only delete if we still own the callback (another instance may have registered newer ones)
      if (window.JK && callbacksRef.current) {
        Object.keys(callbacksRef.current).forEach(key => {
          if (window.JK[key] === callbacksRef.current[key]) {
            delete window.JK[key];
          }
        });
      }
    };
  }, [handleRecordingStartResult, handleRecordingStopResult, handleRecordingStopped, handleRecordingStarted, handleRecordingAborted]);

  return {
    // State
    currentlyRecording,
    startingRecording,
    stoppingRecording,
    currentRecordingId,
    currentOrLastRecordingId,

    // Functions
    startRecording,
    stopRecording,
    abortRecording,
    reset,
    isRecording,
    getThisClientStartedRecording,
    stopRecordingIfNeeded,
    getCurrentRecordingState,
    getState,
  };
};

export default useRecordingHelpers;
