import { useQueryClient } from "@tanstack/react-query";
import { useMemo, useRef, useState } from "react";
import { toast } from "react-hot-toast";
import { useAuth } from "@/contexts/AuthContext";
import useCurrentUser from "@/hooks/useCurrentUser";
import { memoKeys } from "@/hooks/useMemoQueries";
import { userKeys } from "@/hooks/useUserQueries";
import { handleError } from "@/lib/error";
import { cn } from "@/lib/utils";
import { useTranslate } from "@/utils/i18n";
import { convertVisibilityFromString } from "@/utils/memo";
import {
  EditorContent,
  EditorMetadata,
  EditorToolbar,
  FocusModeExitButton,
  FocusModeOverlay,
  TimestampPopover,
  VoiceRecorderPanel,
} from "./components";
import { FOCUS_MODE_STYLES } from "./constants";
import type { EditorRefActions } from "./Editor";
import { useAutoSave, useFocusMode, useKeyboard, useMemoInit, useVoiceRecorder } from "./hooks";
import { cacheService, errorService, memoService, validationService } from "./services";
import { EditorProvider, useEditorContext } from "./state";
import type { MemoEditorProps } from "./types";

const MemoEditor = (props: MemoEditorProps) => (
  <EditorProvider>
    <MemoEditorImpl {...props} />
  </EditorProvider>
);

const MemoEditorImpl: React.FC<MemoEditorProps> = ({
  className,
  cacheKey,
  memo,
  parentMemoName,
  autoFocus,
  placeholder,
  onConfirm,
  onCancel,
}) => {
  const t = useTranslate();
  const queryClient = useQueryClient();
  const currentUser = useCurrentUser();
  const editorRef = useRef<EditorRefActions>(null);
  const { state, actions, dispatch } = useEditorContext();
  const { userGeneralSetting } = useAuth();
  const [isVoiceRecorderOpen, setIsVoiceRecorderOpen] = useState(false);

  const memoName = memo?.name;

  // Get default visibility from user settings
  const defaultVisibility = userGeneralSetting?.memoVisibility ? convertVisibilityFromString(userGeneralSetting.memoVisibility) : undefined;

  useMemoInit({ editorRef, memo, cacheKey, username: currentUser?.name ?? "", autoFocus, defaultVisibility });

  // Auto-save content to localStorage
  useAutoSave(state.content, currentUser?.name ?? "", cacheKey);

  // Focus mode management with body scroll lock
  useFocusMode(state.ui.isFocusMode);

  const voiceRecorderActions = useMemo(
    () => ({
      setVoiceRecorderSupport: (value: boolean) => dispatch(actions.setVoiceRecorderSupport(value)),
      setVoiceRecorderPermission: (value: "unknown" | "granted" | "denied") => dispatch(actions.setVoiceRecorderPermission(value)),
      setVoiceRecorderStatus: (value: "idle" | "requesting_permission" | "recording" | "recorded" | "error" | "unsupported") =>
        dispatch(actions.setVoiceRecorderStatus(value)),
      setVoiceRecorderElapsed: (value: number) => dispatch(actions.setVoiceRecorderElapsed(value)),
      setVoiceRecorderError: (value?: string) => dispatch(actions.setVoiceRecorderError(value)),
      setVoiceRecording: (value?: typeof state.voiceRecorder.recording) => dispatch(actions.setVoiceRecording(value)),
    }),
    [actions, dispatch],
  );

  const voiceRecorder = useVoiceRecorder(voiceRecorderActions);

  const handleToggleFocusMode = () => {
    dispatch(actions.toggleFocusMode());
  };

  const handleStartVoiceRecording = async () => {
    setIsVoiceRecorderOpen(true);
    await voiceRecorder.startRecording();
  };

  const handleVoiceRecorderClick = () => {
    setIsVoiceRecorderOpen(true);

    if (
      state.voiceRecorder.status === "recording" ||
      state.voiceRecorder.status === "requesting_permission" ||
      state.voiceRecorder.status === "recorded"
    ) {
      return;
    }

    void handleStartVoiceRecording();
  };

  const handleKeepVoiceRecording = () => {
    const recording = state.voiceRecorder.recording;
    if (!recording) {
      return;
    }

    dispatch(actions.addLocalFile(recording.localFile));
    voiceRecorder.resetRecording();
    setIsVoiceRecorderOpen(false);
  };

  const handleDiscardVoiceRecording = () => {
    voiceRecorder.resetRecording();
    setIsVoiceRecorderOpen(false);
  };

  const handleCloseVoiceRecorder = () => {
    if (state.voiceRecorder.status === "recording" || state.voiceRecorder.status === "requesting_permission") {
      return;
    }

    voiceRecorder.resetRecording();
    setIsVoiceRecorderOpen(false);
  };

  const handleRecordAgain = async () => {
    voiceRecorder.resetRecording();
    await handleStartVoiceRecording();
  };

  useKeyboard(editorRef, handleSave);

  async function handleSave() {
    // Validate before saving
    const { valid, reason } = validationService.canSave(state);
    if (!valid) {
      toast.error(reason || "Cannot save");
      return;
    }

    dispatch(actions.setLoading("saving", true));

    try {
      const result = await memoService.save(state, { memoName, parentMemoName });

      if (!result.hasChanges) {
        toast.error(t("editor.no-changes-detected"));
        onCancel?.();
        return;
      }

      // Clear localStorage cache on successful save
      cacheService.clear(cacheService.key(currentUser?.name ?? "", cacheKey));

      // Invalidate React Query cache to refresh memo lists across the app
      const invalidationPromises = [
        queryClient.invalidateQueries({ queryKey: memoKeys.lists() }),
        queryClient.invalidateQueries({ queryKey: userKeys.stats() }),
      ];

      // Ensure memo detail pages don't keep stale cached content after edits.
      if (memoName) {
        invalidationPromises.push(queryClient.invalidateQueries({ queryKey: memoKeys.detail(memoName) }));
      }

      // If this was a comment, also invalidate the comments query for the parent memo
      if (parentMemoName) {
        invalidationPromises.push(queryClient.invalidateQueries({ queryKey: memoKeys.comments(parentMemoName) }));
      }

      await Promise.all(invalidationPromises);

      // Reset editor state to initial values
      dispatch(actions.reset());
      if (!memoName && defaultVisibility) {
        dispatch(actions.setMetadata({ visibility: defaultVisibility }));
      }

      // Notify parent component of successful save
      onConfirm?.(result.memoName);
    } catch (error) {
      handleError(error, toast.error, {
        context: "Failed to save memo",
        fallbackMessage: errorService.getErrorMessage(error),
      });
    } finally {
      dispatch(actions.setLoading("saving", false));
    }
  }

  return (
    <>
      <FocusModeOverlay isActive={state.ui.isFocusMode} onToggle={handleToggleFocusMode} />

      {/*
        Layout structure:
        - Uses justify-between to push content to top and bottom
        - In focus mode: becomes fixed with specific spacing, editor grows to fill space
        - In normal mode: stays relative with max-height constraint
      */}
      <div
        className={cn(
          "group relative w-full flex flex-col justify-between items-start bg-card px-4 pt-3 pb-1 rounded-lg border border-border gap-2",
          FOCUS_MODE_STYLES.transition,
          state.ui.isFocusMode && cn(FOCUS_MODE_STYLES.container.base, FOCUS_MODE_STYLES.container.spacing),
          className,
        )}
      >
        {/* Exit button is absolutely positioned in top-right corner when active */}
        <FocusModeExitButton isActive={state.ui.isFocusMode} onToggle={handleToggleFocusMode} title={t("editor.exit-focus-mode")} />

        {memoName && (
          <div className="w-full -mb-1">
            <TimestampPopover />
          </div>
        )}

        {/* Editor content grows to fill available space in focus mode */}
        <EditorContent ref={editorRef} placeholder={placeholder} />

        {isVoiceRecorderOpen && (
          <VoiceRecorderPanel
            voiceRecorder={state.voiceRecorder}
            onStart={() => void handleStartVoiceRecording()}
            onStop={voiceRecorder.stopRecording}
            onKeep={handleKeepVoiceRecording}
            onDiscard={handleDiscardVoiceRecording}
            onRecordAgain={() => void handleRecordAgain()}
            onClose={handleCloseVoiceRecorder}
          />
        )}

        {/* Metadata and toolbar grouped together at bottom */}
        <div className="w-full flex flex-col gap-2">
          <EditorMetadata memoName={memoName} />
          <EditorToolbar onSave={handleSave} onCancel={onCancel} memoName={memoName} onVoiceRecorderClick={handleVoiceRecorderClick} />
        </div>
      </div>
    </>
  );
};

export default MemoEditor;
