import copy from "copy-to-clipboard";
import { isEqual } from "lodash-es";
import { LoaderIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
import useLocalStorage from "react-use/lib/useLocalStorage";
import { Button } from "@/components/ui/button";
import useCurrentUser from "@/hooks/useCurrentUser";
import { cn } from "@/lib/utils";
import { extractMemoIdFromName } from "@/store/common";
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n";
import DateTimeInput from "../DateTimeInput";
import { AttachmentList, LocationDisplay, RelationList } from "../memo-metadata";
import { ErrorBoundary, FocusModeExitButton, FocusModeOverlay } from "./components";
import { FOCUS_MODE_STYLES, LOCALSTORAGE_DEBOUNCE_DELAY } from "./constants";
import Editor, { type EditorRefActions } from "./Editor";
import {
  useDragAndDrop,
  useFocusMode,
  useLocalFileManager,
  useMemoEditorHandlers,
  useMemoEditorInit,
  useMemoEditorKeyboard,
  useMemoEditorState,
  useMemoSave,
} from "./hooks";
import InsertMenu from "./Toolbar/InsertMenu";
import VisibilitySelector from "./Toolbar/VisibilitySelector";
import { MemoEditorContext } from "./types";

export interface Props {
  className?: string;
  cacheKey?: string;
  placeholder?: string;
  memoName?: string;
  parentMemoName?: string;
  autoFocus?: boolean;
  onConfirm?: (memoName: string) => void;
  onCancel?: () => void;
}

const MemoEditor = observer((props: Props) => {
  const { className, cacheKey, memoName, parentMemoName, autoFocus, onConfirm, onCancel } = props;
  const t = useTranslate();
  const { i18n } = useTranslation();
  const currentUser = useCurrentUser();
  const editorRef = useRef<EditorRefActions>(null);

  // Content caching
  const contentCacheKey = `${currentUser.name}-${cacheKey || ""}`;
  const [contentCache, setContentCache] = useLocalStorage<string>(contentCacheKey, "");
  const [hasContent, setHasContent] = useState<boolean>(false);

  // Custom hooks for file management
  const { localFiles, addFiles, removeFile, clearFiles } = useLocalFileManager();

  // Custom hooks for state management
  const {
    memoVisibility,
    attachmentList,
    relationList,
    location,
    isFocusMode,
    isUploadingAttachment,
    isRequesting,
    isComposing,
    isDraggingFile,
    setMemoVisibility,
    setAttachmentList,
    setRelationList,
    setLocation,
    toggleFocusMode,
    setUploadingAttachment,
    setRequesting,
    setComposing,
    setDraggingFile,
    resetState,
  } = useMemoEditorState();

  // Event handlers hook
  const { handleCompositionStart, handleCompositionEnd, handlePasteEvent, handleEditorFocus } = useMemoEditorHandlers({
    editorRef,
    onContentChange: (content: string) => {
      setHasContent(content !== "");
      saveContentToCache(content);
    },
    onFilesAdded: addFiles,
    setComposing,
  });

  // Initialization hook
  const { createTime, updateTime, setCreateTime, setUpdateTime } = useMemoEditorInit({
    editorRef,
    memoName,
    parentMemoName,
    contentCache,
    autoFocus,
    onEditorFocus: handleEditorFocus,
    onVisibilityChange: setMemoVisibility,
    onAttachmentsChange: setAttachmentList,
    onRelationsChange: setRelationList,
    onLocationChange: setLocation,
  });

  // Memo save hook - handles create/update logic
  const { saveMemo } = useMemoSave({
    onUploadingChange: setUploadingAttachment,
    onRequestingChange: setRequesting,
    onSuccess: useCallback(
      (savedMemoName: string) => {
        editorRef.current?.setContent("");
        clearFiles();
        localStorage.removeItem(contentCacheKey);
        if (onConfirm) onConfirm(savedMemoName);
      },
      [clearFiles, contentCacheKey, onConfirm],
    ),
    onCancel: useCallback(() => {
      if (onCancel) onCancel();
    }, [onCancel]),
    onReset: resetState,
    t,
  });

  // Save memo handler
  const handleSaveBtnClick = useCallback(async () => {
    if (isRequesting) {
      return;
    }
    const content = editorRef.current?.getContent() ?? "";
    await saveMemo(content, {
      memoName,
      parentMemoName,
      visibility: memoVisibility,
      attachmentList,
      relationList,
      location,
      localFiles,
      createTime,
      updateTime,
    });
  }, [
    isRequesting,
    saveMemo,
    memoName,
    parentMemoName,
    memoVisibility,
    attachmentList,
    relationList,
    location,
    localFiles,
    createTime,
    updateTime,
  ]);

  // Keyboard shortcuts hook
  const { handleKeyDown } = useMemoEditorKeyboard({
    editorRef,
    isFocusMode,
    isComposing,
    onSave: handleSaveBtnClick,
    onToggleFocusMode: toggleFocusMode,
  });

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

  // Drag-and-drop for file uploads
  const { isDragging, dragHandlers } = useDragAndDrop(addFiles);

  // Sync drag state with component state
  useEffect(() => {
    setDraggingFile(isDragging);
  }, [isDragging, setDraggingFile]);

  // Debounced cache setter
  const cacheTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
  const saveContentToCache = useCallback(
    (content: string) => {
      clearTimeout(cacheTimeoutRef.current);
      cacheTimeoutRef.current = setTimeout(() => {
        if (content !== "") {
          setContentCache(content);
        } else {
          localStorage.removeItem(contentCacheKey);
        }
      }, LOCALSTORAGE_DEBOUNCE_DELAY);
    },
    [contentCacheKey, setContentCache],
  );

  // Compute reference relations
  const referenceRelations = useMemo(() => {
    if (memoName) {
      return relationList.filter(
        (relation) =>
          relation.memo?.name === memoName && relation.relatedMemo?.name !== memoName && relation.type === MemoRelation_Type.REFERENCE,
      );
    }
    return relationList.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
  }, [memoName, relationList]);

  const editorConfig = useMemo(
    () => ({
      className: "",
      initialContent: "",
      placeholder: props.placeholder ?? t("editor.any-thoughts"),
      onContentChange: (content: string) => {
        setHasContent(content !== "");
        saveContentToCache(content);
      },
      onPaste: handlePasteEvent,
      isFocusMode,
      isInIME: isComposing,
      onCompositionStart: handleCompositionStart,
      onCompositionEnd: handleCompositionEnd,
    }),
    [i18n.language, isFocusMode, isComposing, handlePasteEvent, handleCompositionStart, handleCompositionEnd, saveContentToCache],
  );

  const allowSave = (hasContent || attachmentList.length > 0 || localFiles.length > 0) && !isUploadingAttachment && !isRequesting;

  return (
    <ErrorBoundary>
      <MemoEditorContext.Provider
        value={{
          attachmentList,
          relationList,
          setAttachmentList,
          addLocalFiles: (files) => addFiles(Array.from(files.map((f) => f.file))),
          removeLocalFile: removeFile,
          localFiles,
          setRelationList,
          memoName,
        }}
      >
        {/* Focus Mode Backdrop */}
        <FocusModeOverlay isActive={isFocusMode} onToggle={toggleFocusMode} />

        <div
          className={cn(
            "group relative w-full flex flex-col justify-start items-start bg-card px-4 pt-3 pb-2 rounded-lg border",
            FOCUS_MODE_STYLES.transition,
            isDraggingFile ? "border-dashed border-muted-foreground cursor-copy" : "border-border cursor-auto",
            isFocusMode && cn(FOCUS_MODE_STYLES.container.base, FOCUS_MODE_STYLES.container.spacing),
            className,
          )}
          tabIndex={0}
          onKeyDown={handleKeyDown}
          {...dragHandlers}
          onFocus={handleEditorFocus}
        >
          {/* Focus Mode Exit Button */}
          <FocusModeExitButton isActive={isFocusMode} onToggle={toggleFocusMode} title={t("editor.exit-focus-mode")} />

          <Editor ref={editorRef} {...editorConfig} />
          <LocationDisplay mode="edit" location={location} onRemove={() => setLocation(undefined)} />
          {/* Show attachments and pending files together */}
          <AttachmentList
            mode="edit"
            attachments={attachmentList}
            onAttachmentsChange={setAttachmentList}
            localFiles={localFiles}
            onRemoveLocalFile={removeFile}
          />
          <RelationList mode="edit" relations={referenceRelations} onRelationsChange={setRelationList} />
          <div className="relative w-full flex flex-row justify-between items-center pt-2 gap-2" onFocus={(e) => e.stopPropagation()}>
            <div className="flex flex-row justify-start items-center gap-1">
              <InsertMenu
                isUploading={isUploadingAttachment}
                location={location}
                onLocationChange={setLocation}
                onToggleFocusMode={toggleFocusMode}
              />
            </div>
            <div className="shrink-0 flex flex-row justify-end items-center">
              <VisibilitySelector value={memoVisibility} onChange={setMemoVisibility} />
              <div className="flex flex-row justify-end gap-1">
                {props.onCancel && (
                  <Button
                    variant="ghost"
                    disabled={isRequesting}
                    onClick={() => {
                      clearFiles();
                      if (props.onCancel) props.onCancel();
                    }}
                  >
                    {t("common.cancel")}
                  </Button>
                )}
                <Button disabled={!allowSave || isRequesting} onClick={handleSaveBtnClick}>
                  {isRequesting ? <LoaderIcon className="w-4 h-4 animate-spin" /> : t("editor.save")}
                </Button>
              </div>
            </div>
          </div>
        </div>

        {/* Show memo metadata if memoName is provided */}
        {memoName && (
          <div className="w-full -mt-1 mb-4 text-xs leading-5 px-4 opacity-60 font-mono text-muted-foreground">
            <div className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-0.5 items-center">
              {!isEqual(createTime, updateTime) && updateTime && (
                <>
                  <span className="text-left">Updated</span>
                  <DateTimeInput value={updateTime} onChange={setUpdateTime} />
                </>
              )}
              {createTime && (
                <>
                  <span className="text-left">Created</span>
                  <DateTimeInput value={createTime} onChange={setCreateTime} />
                </>
              )}
              <span className="text-left">ID</span>
              <button
                type="button"
                className="px-1 border border-transparent cursor-default text-left"
                onClick={() => {
                  copy(extractMemoIdFromName(memoName));
                  toast.success(t("message.copied"));
                }}
              >
                {extractMemoIdFromName(memoName)}
              </button>
            </div>
          </div>
        )}
      </MemoEditorContext.Provider>
    </ErrorBoundary>
  );
});

export default MemoEditor;
