/* eslint-disable react-hooks/exhaustive-deps */
import React, {
  createContext,
  ReactNode,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import {LayoutRectangle} from 'react-native';
import {useTranslation} from 'react-i18next';
import {SharedValue, useSharedValue, withTiming} from 'react-native-reanimated';
import * as Haptics from 'expo-haptics';
import {useAudioSummary} from '@modules/chats/hooks';
import {
  AI_SUMMARY_MAX_RECORDING_DURATION_MS,
  AI_SUMMARY_MIN_RECORDING_DURATION_MS,
} from '@modules/chats/constants';
import {AiSummaryState} from '@modules/chats/interfaces';
import {showSnackbar} from '@redux/dispatchers';

import {useInputValue} from '../InputValueContext';
import {useAudioRecording} from './AudioRecordingContext';
import {useAudioRecordingAnimated} from './AudioRecordingAnimatedContext';

const ANIMATION_DURATION = 200;

interface AiSummaryContextValue {
  /**
   * Whether the audio summary is active
   */
  isActive: boolean;
  /**
   * Whether the audio summary is processing (compressing, uploading, summarizing)
   */
  isProcessing: boolean;
  /**
   * A shared value to animate initial state of recording
   */
  recordingSharedValue: SharedValue<number>;
  /**
   * A shared value with the current audio metering level (0-1).
   */
  meteringLevel: SharedValue<number>;
  /**
   * Start the audio recording process
   */
  startRecording: () => void;
  /**
   * Finish the audio recording process and summarize the audio
   */
  finishRecording: () => void;
  /**
   * Cancel the audio recording process and clear states
   */
  cancelRecording: () => void;
  /**
   * The current state of the audio summary
   */
  state: AiSummaryState;
  /**
   * Start button layout (x,y coords). Usefull to position the tooltip.
   */
  buttonLayout: Nullable<LayoutRectangle>;
  /**
   * Set the button layout (x,y coords). Usefull to position the tooltip.
   */
  setButtonLayout: (layout: LayoutRectangle) => void;
  /**
   * Whether the current input text is generated from the audio summary or not.
   * is reset on input clear
   */
  isAiGeneratedTextRef: React.RefObject<boolean>;
}

const AiSummaryContext = createContext<AiSummaryContextValue | undefined>(
  undefined,
);

interface AiSummaryProviderProps {
  children: ReactNode;
}

export const AiSummaryProvider: React.FC<AiSummaryProviderProps> = ({
  children,
}) => {
  const {t} = useTranslation();
  const recordingSharedValue = useSharedValue(0);
  const [inputValue, setInputMessage] = useInputValue();
  const isAiGeneratedTextRef = useRef(false);
  const {
    startRecording: startAudioRecording,
    stopRecording: stopAudioRecording,
    cancelRecording: cancelAudioRecording,
    resetAudioRecording,
    meteringLevel,
    saveAudioFromExternalSource,
  } = useAudioRecording();
  const {isLocked} = useAudioRecordingAnimated();
  const {
    summarize,
    state: summaryState,
    clear: clearSummary,
    setAsRecording,
  } = useAudioSummary();
  const isSummaryFinished =
    summaryState.status === 'done' || summaryState.status === 'error';
  const isActive = summaryState.status !== 'idle' && !isSummaryFinished;
  const isProcessing =
    summaryState.status === 'compressing' ||
    summaryState.status === 'uploading' ||
    summaryState.status === 'summarizing';
  const stopAndSummarize = useCallback(async () => {
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    const uri = await stopAudioRecording();
    if (!uri) {
      reset();
      return;
    }
    summarize(uri);
  }, []);
  const [buttonLayout, setButtonLayout] = useState<LayoutRectangle | null>(
    null,
  );

  const startRecording = useCallback(() => {
    isAiGeneratedTextRef.current = false;
    recordingSharedValue.value = withTiming(1, {duration: ANIMATION_DURATION});
    startAudioRecording({
      minMs: AI_SUMMARY_MIN_RECORDING_DURATION_MS,
      maxMs: AI_SUMMARY_MAX_RECORDING_DURATION_MS,
      onMinLimit: () => {
        Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
        showSnackbar({
          title: t('chat.voicenote_ceo.min_duration'),
          variant: 'info',
        });
        reset();
      },
      onMaxLimit: () => {
        showSnackbar({
          title: t('chat.recording.max_limit', {
            max: Math.floor(AI_SUMMARY_MAX_RECORDING_DURATION_MS / 60000),
          }),
          variant: 'info',
        });
        stopAndSummarize();
      },
    });
    setAsRecording();
  }, []);

  const cancelRecording = useCallback(() => {
    Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
    recordingSharedValue.value = withTiming(0, {duration: ANIMATION_DURATION});
    meteringLevel.value = 0;
    cancelAudioRecording();
    clearSummary();
  }, []);

  const finishRecording = useCallback(() => {
    stopAndSummarize();
  }, []);

  const reset = useCallback(() => {
    resetAudioRecording();
    clearSummary();
    recordingSharedValue.value = 0;
  }, []);

  /**
   * Handle the summary state
   */
  useEffect(() => {
    if (summaryState.status === 'done') {
      Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
      setInputMessage(summaryState.summary);
      isAiGeneratedTextRef.current = true;
      reset();
    } else if (summaryState.status === 'error' && summaryState.audioUri) {
      showSnackbar({
        title: t('chat.voicenote_ceo.error'),
        description: t('chat.voicenote_ceo.saved'),
        variant: 'error',
      });
      saveAudioFromExternalSource(summaryState.audioUri);
      isLocked.value = 1;
      // Avoid calling reset() — resetAudioRecording would clear status='stopped'
      // and audioDurationMsRef, leaving the voicenote preview in a broken state.
      clearSummary();
      recordingSharedValue.value = 0;
    } else if (summaryState.status === 'error') {
      showSnackbar({
        title: t('chat.voicenote_ceo.error'),
        description: t('general.error_description'),
        variant: 'error',
      });
    } else if (summaryState.status === 'compressing') {
      recordingSharedValue.value = 0;
      meteringLevel.value = 0;
    }
  }, [summaryState]);

  // Reset the flag on input clear
  useEffect(() => {
    if (inputValue.length === 0 && isAiGeneratedTextRef.current) {
      isAiGeneratedTextRef.current = false;
    }
  }, [inputValue.length === 0]);

  const value = useMemo(
    (): AiSummaryContextValue => ({
      isActive,
      isProcessing,
      state: summaryState,
      recordingSharedValue,
      meteringLevel,
      startRecording,
      finishRecording,
      cancelRecording,
      isAiGeneratedTextRef,
      buttonLayout,
      setButtonLayout,
    }),
    [
      isActive,
      isProcessing,
      recordingSharedValue,
      meteringLevel,
      startRecording,
      finishRecording,
      cancelRecording,
      summaryState,
      isAiGeneratedTextRef,
      buttonLayout,
      setButtonLayout,
    ],
  );

  return (
    <AiSummaryContext.Provider value={value}>
      {children}
    </AiSummaryContext.Provider>
  );
};

export const useAiSummary = (): AiSummaryContextValue => {
  const context = useContext(AiSummaryContext);
  if (!context) {
    throw new Error('useAiSummary must be used within an AiSummaryProvider');
  }
  return context;
};
