import { useQueryClient } from "@tanstack/react-query";
import { useContext, useRef } from "react";
import { toast } from "react-hot-toast";
import { useAuth } from "@/contexts/AuthContext";
import { MemoFilterContext } from "@/contexts/MemoFilterContext";
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 { Visibility } from "@/types/proto/api/v1/memo_service_pb";
import { EditorContent, EditorMetadata, EditorToolbar, FocusModeExitButton, FocusModeOverlay } from "./components";
import { FOCUS_MODE_STYLES } from "./constants";
import type { EditorRefActions } from "./Editor";
import { FloatingFormatToolbar } from "./FloatingFormatToolbar";
import { useAutoSave, useFocusMode, useKeyboard, useMemoInit } from "./hooks";
import { cacheService, errorService, memoService, validationService } from "./services";
import { EditorProvider, useEditorContext } from "./state";
import type { MemoEditorProps } from "./types";

const MemoEditor = (props: MemoEditorProps) => {
  const { className, cacheKey, memoName, parentMemoName, autoFocus, placeholder, onConfirm, onCancel, anonymousId, anonymousName } = props;

  return (
    <EditorProvider>
      <MemoEditorImpl
        className={className}
        cacheKey={cacheKey}
        memoName={memoName}
        parentMemoName={parentMemoName}
        autoFocus={autoFocus}
        placeholder={placeholder}
        onConfirm={onConfirm}
        onCancel={onCancel}
        anonymousId={anonymousId}
        anonymousName={anonymousName}
      />
    </EditorProvider>
  );
};

const MemoEditorImpl: React.FC<MemoEditorProps> = ({
  className,
  cacheKey,
  memoName,
  parentMemoName,
  autoFocus,
  placeholder,
  onConfirm,
  onCancel,
  anonymousId,
  anonymousName,
}) => {
  const t = useTranslate();
  const queryClient = useQueryClient();
  const currentUser = useCurrentUser();
  const editorRef = useRef<EditorRefActions>(null);
  const { state, actions, dispatch } = useEditorContext();
  const { userGeneralSetting } = useAuth();
  const filterContext = useContext(MemoFilterContext);
  const filters = filterContext?.filters || [];

  // Get default visibility from user settings
  // For comments, default to PUBLIC (but allow user to change to PRIVATE)
  // For new memos, use user's default preference
  const defaultVisibility = parentMemoName
    ? Visibility.PUBLIC  // Comments default to PUBLIC
    : (userGeneralSetting?.memoVisibility ? convertVisibilityFromString(userGeneralSetting.memoVisibility) : undefined);

  useMemoInit(editorRef, memoName, cacheKey, 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 handleToggleFocusMode = () => {
    dispatch(actions.toggleFocusMode());
  };

  const handleNewNote = () => {
    dispatch(actions.reset());
    // Small delay so reset completes before focusing
    setTimeout(() => editorRef.current?.focus(), 50);
  };

  useKeyboard(editorRef, { onSave: handleSave, onNewNote: handleNewNote });

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

    // ─── OPTIMISTIC SAVE: Reset UI instantly, sync backend async ─────────
    // Capture state snapshot BEFORE resetting (for the async save)
    const savedState = { ...state };
    const savedContent = state.content;

    // When creating new memo with date filter, use CURRENT timestamp (not midnight)
    const displayTimeFilter = filters.find((f) => f.factor === "displayTime");
    const createTimeOverride = memoName && displayTimeFilter?.value
      ? new Date(displayTimeFilter.value)
      : undefined;

    // 1. INSTANT: Clear localStorage cache + reset editor → user sees empty editor immediately
    cacheService.clear(cacheService.key(currentUser?.name ?? "", cacheKey));
    dispatch(actions.reset());

    // 2. ASYNC: Fire-and-forget backend save + cache invalidation
    //    If save fails, show error toast (user can re-type or check)
    (async () => {
      try {
        const result = await memoService.save(
          savedState,
          { memoName, parentMemoName, anonymousId, anonymousName },
          { createTime: createTimeOverride }
        );

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

        // Invalidate React Query cache (fire-and-forget, don't block)
        void Promise.all([
          queryClient.invalidateQueries({ queryKey: memoKeys.lists(), refetchType: "all" }),
          queryClient.invalidateQueries({ queryKey: userKeys.stats(), refetchType: "all" }),
          ...(parentMemoName ? [
            queryClient.invalidateQueries({ queryKey: memoKeys.comments(parentMemoName), refetchType: "active" }),
            queryClient.invalidateQueries({ queryKey: memoKeys.detail(parentMemoName), refetchType: "active" }),
          ] : []),
        ]);

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

  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
        data-testid="memo-editor"
        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")} />

        {/* Floating format toolbar on text selection */}
        <FloatingFormatToolbar editorRef={editorRef} />

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

        {/* 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} />
        </div>
      </div>
    </>
  );
};

export default MemoEditor;
