'use client';

/* eslint jsx-a11y/click-events-have-key-events: warn */

/* eslint jsx-a11y/no-static-element-interactions: warn */
import {
  Dispatch,
  KeyboardEvent,
  MouseEvent,
  RefObject,
  SetStateAction,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';

import { useBreakpointMd } from '@/hooks/useBreakpoint';
import usePageVisibility from '@/hooks/usePageVisibility';
import useRegenerateLyrics from '@/hooks/useRegenerateLyrics';
import useUploadAndInitializeClip from '@/hooks/useUploadAndInitializeClip';
import makeWavBuffer from '@/lib/makeWavBuffer';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { AudioUploadStatus } from '@/state/createV2Store';

import { useStores } from '../AppProviders';
import useClipPlayer from '../create/createV2/componentsQ3/useClipPlayer';
import UploadStateContext from '../create/uploaderV2/UploadStateContext';
import { ChatInputBarContainer } from './components/input/ChatInputBarContainer';
import { ChatInputModes } from './components/input/ChatInputModes';
import {
  LyricsReference,
  ReferenceType,
} from './components/input/ReferenceTypes';
import { RECORD_COLOR } from './components/input/WaveformConstants';
import {
  OptionConfig,
  useChatInputStore,
  useChatMessagesStore,
} from './stores';
import { useChatContext } from './useChat';
import { useChatMessageActions } from './useChatMessageActions';
import { usePresetsData } from './usePresetsData';
import { formatString, getOptionValue } from './utils';

// Define the possible chat input mode types
export enum ChatInputModeType {
  ACTION = 'action',
  TRANSCRIBE = 'transcribe',
  RECORD = 'record',
  UPLOAD_FILE = 'upload_file',
}

interface ChatInputProps {
  ref?: RefObject<any>;
  className?: string;
  inputClassName?: string;
  referencedClip: any;
  referencedLyrics?: LyricsReference;
  referencedClipDuration: number | undefined;
  onStartRecording?: () => void;
  onStopRecording?: (buffer: AudioBuffer | null) => void;
  onDismissRecording?: () => void;
  onUploadComplete?: (
    clipId: string,
    title: string,
    imageUrl: string,
    uploadId: string,
    audioBuffer?: AudioBuffer
  ) => void;
  backgroundColor?: string;
  chatInputMode: ChatInputModeType;
  setChatInputMode: Dispatch<SetStateAction<ChatInputModeType>>;
  isNewUserState?: boolean;
}

export const ChatInput: React.FC<ChatInputProps> = ({
  ref,
  className,
  inputClassName,
  referencedClip,
  referencedLyrics,
  referencedClipDuration,
  onStopRecording,
  onDismissRecording,
  onUploadComplete,
  backgroundColor,
  chatInputMode,
  setChatInputMode,
  isNewUserState = false,
}) => {
  const inputRef = useRef<HTMLInputElement>(null);
  const uploadAndInitializeClip = useUploadAndInitializeClip();
  const { input, setInput, isVoiceMode, setIsVoiceMode } = useChatInputStore();
  const { setIsLoading } = useChatMessagesStore();
  const { removeReference, chatUUID, addReference, registerClipStatusChange } =
    useChatContext();
  const { handleSend: sendMessage } = useChatMessageActions();
  const { getPlaybackTime } = useClipPlayer(referencedClip?.id || '');
  const regenerateLyrics = useRegenerateLyrics();
  const uploadState = useContext(UploadStateContext);
  const { session } = useStores();
  const isMobile = !useBreakpointMd();
  const hasProcessedUploadRef = useRef(false);
  const presetsData = usePresetsData();
  const suggestionOffsetRef = useRef(0);
  const innerTimerRef = useRef<NodeJS.Timeout | null>(null);
  const outerTimerRef = useRef<NodeJS.Timeout | null>(null);
  const initialOuterTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const isAnimatingRef = useRef(false);
  const currentSuggestionTextRef = useRef<string>('');
  const [visibilityState] = usePageVisibility();

  // Clean up all timers
  const cleanupTimers = useCallback(
    ({
      outerOnly,
      innerOnly,
    }: {
      outerOnly?: boolean;
      innerOnly?: boolean;
    }) => {
      if (innerTimerRef.current !== null && !outerOnly) {
        clearInterval(innerTimerRef.current);
        innerTimerRef.current = null;
      }
      if (outerTimerRef.current !== null && !innerOnly) {
        clearInterval(outerTimerRef.current);
        outerTimerRef.current = null;
      }
      if (initialOuterTimeoutRef.current !== null) {
        clearTimeout(initialOuterTimeoutRef.current);
        initialOuterTimeoutRef.current = null;
      }

      isAnimatingRef.current = false;
    },
    []
  );

  // Handle tab visibility changes
  useEffect(() => {
    if (!isNewUserState) {
      return;
    }

    if (visibilityState === 'hidden') {
      // Pause animation when tab becomes hidden
      cleanupTimers({});
    } else if (visibilityState === 'visible' && isAnimatingRef.current) {
      // Reset state when tab becomes visible again to prevent desync
      cleanupTimers({});
      suggestionOffsetRef.current = 0;
      setInput('');
    }
  }, [visibilityState, isNewUserState, cleanupTimers, setInput]);

  const startAnimationRound = useCallback(() => {
    const suggestion = presetsData.data?.suggestions?.[0];
    if (!suggestion) {
      return;
    }

    // Atomic check-and-set to prevent concurrent animations
    if (isAnimatingRef.current) {
      return;
    }
    isAnimatingRef.current = true;

    const option = Object.entries(
      (suggestion.options_config as Record<string, OptionConfig>) ?? {}
    ).reduce(
      (acc, [key, values]) => {
        const randomOption =
          values.options[Math.floor(Math.random() * values.options.length)];
        acc[key] = getOptionValue(randomOption, values.prefix_with_article);
        return acc;
      },
      {} as Record<string, string>
    );
    const suggestionText = formatString(suggestion.template, option);
    currentSuggestionTextRef.current = suggestionText;
    suggestionOffsetRef.current = suggestionText.length;

    // Clear any existing inner timer before starting a new one
    if (innerTimerRef.current !== null) {
      clearInterval(innerTimerRef.current);
      innerTimerRef.current = null;
    }

    innerTimerRef.current = setInterval(
      () => handleInnerInterval(suggestionText),
      20
    );
  }, [cleanupTimers, presetsData.data, setInput]);

  const handleOuterInterval = useCallback(() => {
    // Don't start new animation if tab is hidden or already animating
    if (visibilityState === 'hidden' || isAnimatingRef.current) {
      return;
    }
    startAnimationRound();
  }, [startAnimationRound, visibilityState]);

  const handleInnerInterval = useCallback(
    (suggestionText: string) => {
      if (visibilityState === 'hidden') {
        cleanupTimers({ innerOnly: true });
        return;
      }

      suggestionOffsetRef.current -= 1;
      setInput(
        suggestionText.slice(
          0,
          suggestionText.length - suggestionOffsetRef.current
        )
      );

      if (suggestionOffsetRef.current === 0) {
        cleanupTimers({ innerOnly: true });
      }
    },
    [visibilityState, cleanupTimers]
  );

  const startTextAnimation = useCallback(() => {
    // Always clear the outer timer first to prevent stacking
    if (outerTimerRef.current !== null) {
      clearInterval(outerTimerRef.current);
      outerTimerRef.current = null;
    }

    // Also clear inner timer to prevent orphaned animations
    if (innerTimerRef.current !== null) {
      clearInterval(innerTimerRef.current);
      innerTimerRef.current = null;
    }

    isAnimatingRef.current = false;

    // Start the recurring outer timer
    initialOuterTimeoutRef.current = setTimeout(() => {
      handleOuterInterval();
      initialOuterTimeoutRef.current = null;
    }, 800);
    outerTimerRef.current = setInterval(handleOuterInterval, 3000);
  }, [handleOuterInterval]);

  useEffect(() => {
    if (!isNewUserState) {
      cleanupTimers({});
      return;
    }

    // Don't start animation if tab is hidden
    if (visibilityState === 'hidden') {
      return;
    }

    startTextAnimation();

    return () => {
      cleanupTimers({});
    };
  }, [
    presetsData.data,
    isNewUserState,
    visibilityState,
    cleanupTimers,
    startTextAnimation,
  ]);

  // useEffect(() => {
  //   if (uploadState.uploadFileConfig) {
  //     setChatInputMode(ChatInputModeType.UPLOAD_FILE);
  //   }
  // }, [uploadState.uploadFileConfig]);

  useEffect(() => {
    if (uploadState.trimmedFile) {
      setChatInputMode(ChatInputModeType.UPLOAD_FILE);
      uploadState.attemptClose();
    }
  }, [uploadState.trimmedFile]);

  useEffect(() => {
    if (
      uploadState.uploadStatus === AudioUploadStatus.COMPLETE &&
      uploadState.pendingClipContext?.clipId &&
      !hasProcessedUploadRef.current
    ) {
      hasProcessedUploadRef.current = true;
      setChatInputMode(ChatInputModeType.ACTION);
      sendMessage({
        message: JSON.stringify({
          clipIds: [uploadState.pendingClipContext?.clipId],
        }),
        clipIds: [uploadState.pendingClipContext?.clipId],
      }).then(() => {
        // Set the recorded audio as referenced audio file after message is sent
        if (uploadState.pendingClipContext?.clipId) {
          addReference({
            type: ReferenceType.CLIP,
            clipId: uploadState.pendingClipContext?.clipId,
          });
          registerClipStatusChange({
            clipId: uploadState.pendingClipContext?.clipId,
            status: 'complete',
            duration: uploadState.uploadFileConfig?.audioBuffer?.duration ?? 0,
            title: uploadState.pendingClipContext?.title ?? '',
          });
        }
      });
      logWebUserEvent(
        {
          actionName: 'OrpheusAudioUploaded',
          context: {
            sessionId: chatUUID,
            fileSizeBytes:
              uploadState.uploadFileConfig?.clientSelectedFile?.size ?? 0,
            fileType:
              uploadState.uploadFileConfig?.clientSelectedFile?.type ??
              'unknown',
            uploadMethod: !!uploadState.uploadFileConfig?.clientSelectedFile
              ? 'file_upload'
              : 'audio_recording',
          },
        },
        session
      );
    }
    if (uploadState.uploadStatus !== AudioUploadStatus.COMPLETE) {
      hasProcessedUploadRef.current = false;
    }
    if (uploadState.uploadStatus === AudioUploadStatus.ERROR) {
      setChatInputMode(ChatInputModeType.ACTION);
    }
  }, [
    uploadState.uploadStatus,
    uploadState.pendingClipContext,
    setChatInputMode,
    sendMessage,
    addReference,
    registerClipStatusChange,
    uploadState.pendingClipContext?.clipId,
    uploadState.uploadFileConfig?.audioBuffer?.duration,
  ]);

  const handleTriggerSend = useCallback(
    async (
      e: KeyboardEvent<HTMLInputElement> | MouseEvent<HTMLButtonElement>
    ) => {
      // if (referencedLyrics && isSelectingLyrics) {
      //   sendMessage({
      //     input,
      //     setInput,
      //     message: `[Lyrics Edit] ${input}`,
      //     needsResponse: false,
      //   });
      //   setIsLoading(true);
      //   const { fullText } = await regenerateLyrics({
      //     prompt: input,
      //     prefix: referencedLyrics.message.slice(
      //       0,
      //       referencedLyrics.selectionOverlayRange.start
      //     ),
      //     suffix: referencedLyrics.message.slice(
      //       referencedLyrics.selectionOverlayRange.end
      //     ),
      //     edit: referencedLyrics.message.slice(
      //       referencedLyrics.selectionOverlayRange.start,
      //       referencedLyrics.selectionOverlayRange.end
      //     ),
      //     title: '',
      //   });
      //   setIsLoading(false);
      //   sendMessage({
      //     input,
      //     setInput,
      //     message: fullText,
      //     needsResponse: false,
      //     toolCallId: referencedLyrics.toolCallId ?? undefined,
      //     isHidden: false,
      //     forcedRole: 'assistant',
      //   });
      //   if (referencedLyrics) {
      //     removeReference(referencedLyrics.id);
      //   }
      //   setInput('');
      //   return;
      // }
      sendMessage({
        input,
        setInput,
        isVoiceMode: isVoiceMode,
        isMobile,
      });
      setIsVoiceMode(false);
      e.preventDefault();
    },
    [
      input,
      referencedLyrics,
      sendMessage,
      setIsLoading,
      regenerateLyrics,
      removeReference,
      setInput,
      isVoiceMode,
      setIsVoiceMode,
      isMobile,
    ]
  );

  // Recording state for border styling
  const [isRecording, setIsRecording] = useState(false);

  // Mode handlers
  // const handleStartRecording = () => {
  //   setChatInputMode(ChatInputModeType.RECORD);
  //   onStartRecording?.();
  // };

  // const handleStartUploadFile = () => {
  //   setChatInputMode(ChatInputModeType.UPLOAD_FILE);
  // };

  const handleStartTranscribing = () => {
    // Enter dedicated TranscribeMode. It manages mic start/stop itself.
    setChatInputMode(ChatInputModeType.TRANSCRIBE);
    // Don't set red border for transcribe mode
  };

  const handleDismissMode = () => {
    setChatInputMode(ChatInputModeType.ACTION);
    onDismissRecording?.();
    setIsRecording(false); // Clear red border when leaving any mode
  };

  const handleUploadComplete = (
    clipId: string,
    title: string,
    imageUrl: string,
    uploadId: string
  ) => {
    setChatInputMode(ChatInputModeType.ACTION);
    onUploadComplete?.(clipId, title, imageUrl, uploadId);
  };

  // Recording lifecycle handlers
  const handleRecordStart = () => {
    setIsRecording(true);
  };

  const handleRecordEnd = () => {
    setIsRecording(false);
  };

  // Render mode content based on current mode
  const renderModeContent = () => {
    switch (chatInputMode) {
      case ChatInputModeType.TRANSCRIBE:
        return (
          <ChatInputModes.TranscribeMode
            onTranscribe={(text) => {
              // Stream partial/final transcripts into the input
              setInput(text);
            }}
            onClose={() => {
              handleDismissMode();
            }}
          />
        );

      case ChatInputModeType.RECORD:
        return (
          <ChatInputModes.RecordMode
            onStopRecording={onStopRecording}
            onDismiss={handleDismissMode}
            onUploadComplete={handleUploadComplete}
            onRecordStart={handleRecordStart}
            onRecordEnd={handleRecordEnd}
            onConfirm={async (buffer) => {
              // Handle ONLY the upload processing logic
              if (buffer) {
                try {
                  // Convert AudioBuffer to WAV file
                  const wav = await makeWavBuffer(buffer);
                  const blob = new Blob([wav], { type: 'audio/wav' });
                  const file = new File([blob], `recording-${Date.now()}.wav`, {
                    type: 'audio/wav',
                  });

                  // Upload the file and get the result
                  const result = await uploadAndInitializeClip(
                    file,
                    'audio_recording'
                  );

                  // Log audio upload event
                  logWebUserEvent(
                    {
                      actionName: 'OrpheusAudioUploaded',
                      context: {
                        sessionId: chatUUID,
                        fileSizeBytes: file.size,
                        fileType: file.type,
                        uploadMethod: 'audio_recording',
                      },
                    },
                    session
                  );

                  // Call the legacy callback if provided
                  onUploadComplete?.(
                    result.clipId,
                    result.title,
                    result.imageUrl,
                    result.uploadId,
                    buffer
                  );
                } catch (error) {
                  console.error('Upload failed in ChatInput:', error);
                  // We handle the error elsewhere by showing a toast.
                }
              }
            }}
            onClose={() => {
              // Switch back to action mode
              setChatInputMode(ChatInputModeType.ACTION);
            }}
          />
        );

      case ChatInputModeType.UPLOAD_FILE:
        return (
          <ChatInputModes.UploadFileMode
            onUploadComplete={handleUploadComplete}
            onClose={() => {
              // Switch back to action mode
              setChatInputMode(ChatInputModeType.ACTION);
            }}
            uploadFileConfig={uploadState.uploadFileConfig ?? undefined}
          />
        );

      case ChatInputModeType.ACTION:
      default:
        return (
          <ChatInputModes.ActionMode
            inputClassName={inputClassName}
            handleTriggerSend={handleTriggerSend}
            onStartTranscribe={handleStartTranscribing}
            referencedClip={referencedClip}
            referencedClipDuration={referencedClipDuration}
            getPlaybackTime={getPlaybackTime}
            onStopTextAnimation={() => {
              cleanupTimers({ outerOnly: true });
            }}
            onStartTextAnimation={() => {
              if (isNewUserState) {
                startTextAnimation();
              }
            }}
            //onStartRecording={handleStartRecording}
            //onStartUploadFile={handleStartUploadFile}
          />
        );
    }
  };

  return (
    <ChatInputBarContainer
      borderColor={isRecording ? RECORD_COLOR : undefined}
      backgroundColor={backgroundColor}
      className={className}
      isNewUserState={isNewUserState}
      ref={ref}
    >
      <div
        onClick={() => {
          if (chatInputMode === ChatInputModeType.ACTION) {
            inputRef.current?.focus();
          }
        }}
      >
        {/* Mode-based Content Rendering */}
        {renderModeContent()}
      </div>
    </ChatInputBarContainer>
  );
};
