import React, { useState, useRef, useEffect, useCallback } from "react";
import { Icon } from "@iconify/react";
import { Note } from "../../types/note";
import {
  parseMarkdown,
  makeTagsClickable,
  highlightSearchText,
} from "../../utils/markdown";
import { parseSQLiteUtc, formatLocalDateTime } from "../../utils/dateFormat";
import { useMarkdown } from "../../hooks/useMarkdown";
import { useAppSelector, useAppDispatch } from "../../store/hooks";
import { Modal } from "../common/Modal";
import { offlineNotesApi } from "../../api/offlineNotesApi";
import { useNotification } from "../../hooks/useNotification";
import { setSelectedTag } from "../../store/slices/notesSlice";
import { getImageUrl, getFileUrl } from "../../utils/filePaths";
import { MarkdownToolbar } from "./MarkdownToolbar";
import { FloatingToolbar } from "./FloatingToolbar";
import { NotePreview } from "./NotePreview";
import { ImageUpload } from "./ImageUpload";
import { FileUpload } from "./FileUpload";
import { aiApi } from "../../api/aiApi";
import { GenerateTagsModal } from "./GenerateTagsModal";
import { ImproveTextModal } from "./ImproveTextModal";
import { extractTags } from "../../utils/markdown";

interface NoteItemProps {
  note: Note;
  onDelete: (id: number | string) => void;
  onPin: (id: number | string) => void;
  onArchive: (id: number | string) => void;
  onReload: () => void;
  isSelected?: boolean;
  onSelect?: (id: number | string) => void;
  onFocusNote?: (id: number | string) => void;
}

export const NoteItem: React.FC<NoteItemProps> = ({
  note,
  onDelete: _onDelete,
  onPin,
  onArchive,
  onReload,
  isSelected = false,
  onSelect,
  onFocusNote,
}) => {
  const [isEditing, setIsEditing] = useState(false);
  const [editContent, setEditContent] = useState(note.content);
  const [editIsPrivate, setEditIsPrivate] = useState(
    note.is_private !== undefined ? note.is_private === 1 : true
  );
  const [showArchiveModal, setShowArchiveModal] = useState(false);
  const [editImages, setEditImages] = useState<File[]>([]);
  const [editFiles, setEditFiles] = useState<File[]>([]);
  const [deletedImageIds, setDeletedImageIds] = useState<(number | string)[]>(
    []
  );
  const [deletedFileIds, setDeletedFileIds] = useState<(number | string)[]>([]);
  const [isAiLoading, setIsAiLoading] = useState(false);
  const [showFloatingToolbar, setShowFloatingToolbar] = useState(false);
  const [toolbarPosition, setToolbarPosition] = useState({ top: 0, left: 0 });
  const [hasSelection, setHasSelection] = useState(false);
  const [activeFormats, setActiveFormats] = useState({
    bold: false,
    italic: false,
    strikethrough: false,
  });
  const [localPreviewMode, setLocalPreviewMode] = useState(false);
  const [isExpanded, setIsExpanded] = useState(false);
  const [isLongNote, setIsLongNote] = useState(false);
  const [showTagsModal, setShowTagsModal] = useState(false);
  const [suggestedTags, setSuggestedTags] = useState<string[]>([]);
  const [isGeneratingTags, setIsGeneratingTags] = useState(false);
  const [tagsGenerationError, setTagsGenerationError] = useState(false);
  const [showImproveModal, setShowImproveModal] = useState(false);
  const [improvedText, setImprovedText] = useState("");
  const [improveError, setImproveError] = useState(false);
  const [improveErrorMessage, setImproveErrorMessage] = useState<string>("");
  const editTextareaRef = useRef<HTMLTextAreaElement>(null);
  const imageInputRef = useRef<HTMLInputElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const textNoteRef = useRef<HTMLDivElement>(null);
  const shouldSetCursorToEndRef = useRef(false);
  const searchQuery = useAppSelector((state) => state.notes.searchQuery);
  const isPreviewMode = useAppSelector((state) => state.ui.isPreviewMode);
  const aiEnabled = useAppSelector((state) => state.profile.aiEnabled);
  const user = useAppSelector((state) => state.profile.user);
  const { showNotification } = useNotification();
  const dispatch = useAppDispatch();
  useMarkdown({ onNoteUpdate: onReload }); // Инициализируем обработчики спойлеров, внешних ссылок и чекбоксов

  // Проверяем, включена ли плавающая панель
  const floatingToolbarEnabled =
    user?.floating_toolbar_enabled !== undefined
      ? user.floating_toolbar_enabled === 1
      : true;

  const handleEdit = () => {
    setIsEditing(true);
    setEditContent(note.content);
    setEditIsPrivate(
      note.is_private !== undefined ? note.is_private === 1 : true
    );
    setEditImages([]);
    setEditFiles([]);
    setDeletedImageIds([]);
    setDeletedFileIds([]);
    setShowFloatingToolbar(false);
    setActiveFormats({ bold: false, italic: false, strikethrough: false });
    setLocalPreviewMode(false);
    shouldSetCursorToEndRef.current = true; // Устанавливаем флаг для установки курсора в конец
  };

  const toggleLocalPreviewMode = () => {
    setLocalPreviewMode(!localPreviewMode);
    setShowFloatingToolbar(false);
  };

  const handleSaveEdit = async () => {
    if (!editContent.trim()) {
      showNotification("Введите текст заметки", "warning");
      return;
    }

    try {
      await offlineNotesApi.update(
        note.id,
        editContent,
        false,
        editIsPrivate ? 1 : 0
      );

      // Удаляем выбранные изображения
      for (const imageId of deletedImageIds) {
        await offlineNotesApi.deleteImage(note.id, imageId);
      }

      // Удаляем выбранные файлы
      for (const fileId of deletedFileIds) {
        await offlineNotesApi.deleteFile(note.id, fileId);
      }

      // Загружаем новые изображения
      if (editImages.length > 0) {
        await offlineNotesApi.uploadImages(note.id, editImages);
      }

      // Загружаем новые файлы
      if (editFiles.length > 0) {
        await offlineNotesApi.uploadFiles(note.id, editFiles);
      }

      showNotification("Заметка обновлена!", "success");
      setIsEditing(false);
      setEditImages([]);
      setEditFiles([]);
      setDeletedImageIds([]);
      setDeletedFileIds([]);
      onReload();
      // Устанавливаем фокус на заметку после сохранения
      if (onFocusNote) {
        // Небольшая задержка для завершения обновления DOM
        setTimeout(() => {
          onFocusNote(note.id);
        }, 100);
      }
    } catch (error) {
      console.error("Ошибка обновления заметки:", error);
      showNotification("Ошибка обновления заметки", "error");
    }
  };

  const handleCancelEdit = () => {
    setIsEditing(false);
    setEditContent(note.content);
    setEditIsPrivate(
      note.is_private !== undefined ? note.is_private === 1 : true
    );
    setEditImages([]);
    setEditFiles([]);
    setDeletedImageIds([]);
    setDeletedFileIds([]);
    setShowFloatingToolbar(false);
    setActiveFormats({ bold: false, italic: false, strikethrough: false });
    setLocalPreviewMode(false);
  };

  const handleDeleteExistingImage = (imageId: number | string) => {
    setDeletedImageIds([...deletedImageIds, imageId]);
  };

  const handleDeleteExistingFile = (fileId: number | string) => {
    setDeletedFileIds([...deletedFileIds, fileId]);
  };

  const handleRestoreImage = (imageId: number | string) => {
    setDeletedImageIds(deletedImageIds.filter((id) => id !== imageId));
  };

  const handleRestoreFile = (fileId: number | string) => {
    setDeletedFileIds(deletedFileIds.filter((id) => id !== fileId));
  };

  const handleAiImprove = async () => {
    if (!editContent.trim()) {
      showNotification("Введите текст для улучшения", "warning");
      return;
    }

    setIsAiLoading(true);
    setImproveError(false);
    setImproveErrorMessage("");
    setImprovedText("");
    setShowImproveModal(true);
    try {
      const improved = await aiApi.improveText(editContent);
      setImprovedText(improved);
      setImproveError(false);
    } catch (error: any) {
      console.error("Ошибка улучшения текста:", error);
      setImproveError(true);
      setImproveErrorMessage(
        error.response?.data?.error ||
          error.message ||
          "Ошибка улучшения текста"
      );
    } finally {
      setIsAiLoading(false);
    }
  };

  const handleApplyImprovedText = (text: string) => {
    setEditContent(text);
    showNotification("Текст улучшен!", "success");
    setShowImproveModal(false);
    setImprovedText("");
    setImproveError(false);
    setImproveErrorMessage("");
  };

  const handleGenerateTags = async () => {
    if (!editContent.trim()) {
      showNotification("Введите текст для генерации тегов", "warning");
      return;
    }

    setIsGeneratingTags(true);
    setTagsGenerationError(false);
    setSuggestedTags([]);
    setShowTagsModal(true);
    try {
      const tags = await aiApi.generateTags(editContent);
      if (tags && tags.length > 0) {
        setSuggestedTags(tags);
        setTagsGenerationError(false);
      } else {
        setTagsGenerationError(true);
        setShowTagsModal(false);
        showNotification("ИИ не смог предложить теги для этой заметки", "info");
      }
    } catch (error: any) {
      console.error("Ошибка генерации тегов:", error);
      console.error("Детали ошибки:", error.response?.data);
      setTagsGenerationError(true);
      setShowTagsModal(false);
      const errorMessage =
        error.response?.data?.error ||
        error.message ||
        "Ошибка генерации тегов";
      showNotification(errorMessage, "error");
    } finally {
      setIsGeneratingTags(false);
    }
  };

  const handleSelectTags = (tags: string[]) => {
    if (tags.length === 0) return;

    const existingTags = extractTags(editContent);
    const tagsToAdd = tags
      .filter(
        (tag) =>
          !existingTags.some(
            (existing) => existing.toLowerCase() === tag.toLowerCase()
          )
      )
      .map((tag) => `#${tag}`)
      .join(" ");

    if (tagsToAdd) {
      // Добавляем теги в конец заметки
      const newContent =
        editContent.trim() + (editContent.trim() ? "\n\n" : "") + tagsToAdd;
      setEditContent(newContent);
      showNotification(`Добавлено тегов: ${tags.length}`, "success");
    } else {
      showNotification("Все предлагаемые теги уже есть в заметке", "info");
    }
  };

  // Функция для определения активных форматов в выделенном тексте
  const getActiveFormats = useCallback(() => {
    const textarea = editTextareaRef.current;
    if (!textarea) {
      return { bold: false, italic: false, strikethrough: false };
    }

    const start = textarea.selectionStart;
    const end = textarea.selectionEnd;

    // Если нет выделения, возвращаем все false
    if (start === end) {
      return { bold: false, italic: false, strikethrough: false };
    }

    const selectedText = editContent.substring(start, end);

    // Инициализируем форматы
    const formats = {
      bold: false,
      italic: false,
      strikethrough: false,
    };

    // Расширяем область проверки для захвата всех возможных тегов
    const checkRange = 10; // Проверяем до 10 символов перед и после
    const checkStart = Math.max(0, start - checkRange);
    const checkEnd = Math.min(editContent.length, end + checkRange);
    const contextText = editContent.substring(checkStart, checkEnd);
    const selectionInContext = start - checkStart;

    // Текст вокруг выделения
    const beforeContext = contextText.substring(0, selectionInContext);
    const afterContext = contextText.substring(
      selectionInContext + selectedText.length
    );

    // Функция для подсчета символов в конце строки
    const countTrailingChars = (text: string, char: string) => {
      let count = 0;
      for (let i = text.length - 1; i >= 0; i--) {
        if (text[i] === char) count++;
        else break;
      }
      return count;
    };

    // Функция для подсчета символов в начале строки
    const countLeadingChars = (text: string, char: string) => {
      let count = 0;
      for (let i = 0; i < text.length; i++) {
        if (text[i] === char) count++;
        else break;
      }
      return count;
    };

    // Проверяем зачеркнутый (~~) - проверяем первым, так как он не пересекается с другими
    const strikethroughBefore = beforeContext.slice(-2);
    const strikethroughAfter = afterContext.slice(0, 2);
    const hasStrikethroughAround =
      strikethroughBefore === "~~" && strikethroughAfter === "~~";
    const hasStrikethroughInside =
      selectedText.startsWith("~~") &&
      selectedText.endsWith("~~") &&
      selectedText.length >= 4;

    if (hasStrikethroughAround || hasStrikethroughInside) {
      formats.strikethrough = true;
    }

    // Проверяем звездочки для жирного и курсива
    // Подсчитываем количество звездочек в конце контекста перед выделением
    const trailingStars = countTrailingChars(beforeContext, "*");
    const leadingStars = countLeadingChars(afterContext, "*");
    const leadingStarsInSelection = countLeadingChars(selectedText, "*");
    const trailingStarsInSelection = countTrailingChars(selectedText, "*");

    // Проверяем жирный (**)
    // Жирный требует минимум 2 звездочки с каждой стороны
    const hasBoldStarsBefore = trailingStars >= 2;
    const hasBoldStarsAfter = leadingStars >= 2;
    const hasBoldStarsInSelection =
      leadingStarsInSelection >= 2 && trailingStarsInSelection >= 2;

    // Проверяем, что это действительно жирный
    if (hasBoldStarsBefore && hasBoldStarsAfter) {
      formats.bold = true;
    } else if (hasBoldStarsInSelection && selectedText.length >= 4) {
      formats.bold = true;
    }

    // Проверяем курсив (*)
    // Если есть 3+ звездочки с каждой стороны, это комбинация жирного и курсива
    // Если есть 1 звездочка с каждой стороны (и не 2), это курсив
    // Если есть 3+ звездочки, это жирный + курсив
    const exactItalicBefore =
      trailingStars === 1 || (trailingStars >= 3 && trailingStars % 2 === 1);
    const exactItalicAfter =
      leadingStars === 1 || (leadingStars >= 3 && leadingStars % 2 === 1);
    const exactItalicInSelection =
      (leadingStarsInSelection === 1 && trailingStarsInSelection === 1) ||
      (leadingStarsInSelection >= 3 &&
        trailingStarsInSelection >= 3 &&
        leadingStarsInSelection % 2 === 1 &&
        trailingStarsInSelection % 2 === 1);

    // Проверяем также внутри выделения
    if (exactItalicBefore && exactItalicAfter && !formats.bold) {
      // Если перед и после по 1 звездочке, это курсив
      formats.italic = true;
    } else if (trailingStars >= 3 && leadingStars >= 3) {
      // Если по 3+ звездочки, это комбинация жирного и курсива
      formats.italic = true;
      formats.bold = true;
    } else if (exactItalicInSelection && selectedText.length >= 2) {
      formats.italic = true;
    } else if (
      leadingStarsInSelection === 1 &&
      trailingStarsInSelection === 1 &&
      selectedText.length >= 2 &&
      !selectedText.startsWith("**") &&
      !selectedText.endsWith("**")
    ) {
      formats.italic = true;
    }

    // Если определен жирный, но не определен курсив, и есть 3+ звездочки, значит курсив тоже есть
    if (
      formats.bold &&
      (trailingStars >= 3 ||
        leadingStars >= 3 ||
        leadingStarsInSelection >= 3 ||
        trailingStarsInSelection >= 3)
    ) {
      formats.italic = true;
    }

    return formats;
  }, [editContent]);

  const insertMarkdown = useCallback(
    (before: string, after: string = "") => {
      const textarea = editTextareaRef.current;
      if (!textarea) return;

      const start = textarea.selectionStart;
      const end = textarea.selectionEnd;
      const selectedText = editContent.substring(start, end);

      const tagLength = before.length;

      // Проверяем область вокруг выделения (расширяем для проверки тегов)
      const checkStart = Math.max(0, start - tagLength);
      const checkEnd = Math.min(editContent.length, end + tagLength);
      const contextText = editContent.substring(checkStart, checkEnd);
      const selectionInContext = start - checkStart;

      // Текст перед и после выделения в контексте
      const beforeContext = contextText.substring(0, selectionInContext);
      const afterContext = contextText.substring(
        selectionInContext + selectedText.length
      );

      // Проверяем точное совпадение тегов
      const hasTagsBefore = beforeContext.endsWith(before);
      const hasTagsAfter = afterContext.startsWith(after);

      // Проверяем, есть ли теги внутри выделенного текста
      const startsWithTag = selectedText.startsWith(before);
      const endsWithTag = selectedText.endsWith(after);

      // Определяем, нужно ли удалять теги
      let shouldRemoveTags = false;

      if (before === "*" && after === "*") {
        // Для курсива нужно убедиться, что это не часть ** (жирного)
        const beforeBeforeChar = start > 1 ? editContent[start - 2] : "";
        const afterAfterChar =
          end + 1 < editContent.length ? editContent[end + 1] : "";

        // Проверяем, что перед * не стоит еще один *, и после * не стоит еще один *
        const isItalicBefore = hasTagsBefore && beforeBeforeChar !== "*";
        const isItalicAfter = hasTagsAfter && afterAfterChar !== "*";

        // Проверяем внутри выделения
        const hasItalicInside =
          startsWithTag &&
          endsWithTag &&
          selectedText.length >= 2 &&
          !selectedText.startsWith("**") &&
          !selectedText.endsWith("**");

        shouldRemoveTags = (isItalicBefore && isItalicAfter) || hasItalicInside;
      } else if (before === "**" && after === "**") {
        // Для жирного проверяем точное совпадение **
        shouldRemoveTags =
          (hasTagsBefore && hasTagsAfter) ||
          (startsWithTag && endsWithTag && selectedText.length >= 4);
      } else if (before === "~~" && after === "~~") {
        // Для зачеркнутого проверяем точное совпадение ~~
        shouldRemoveTags =
          (hasTagsBefore && hasTagsAfter) ||
          (startsWithTag && endsWithTag && selectedText.length >= 4);
      } else {
        // Для других тегов (например, ||)
        shouldRemoveTags =
          (hasTagsBefore && hasTagsAfter) ||
          (startsWithTag &&
            endsWithTag &&
            selectedText.length >= tagLength * 2);
      }

      let newText: string;
      let newStart: number;
      let newEnd: number;

      if (shouldRemoveTags) {
        // Удаляем существующие теги
        if (hasTagsBefore && hasTagsAfter) {
          // Теги находятся вокруг выделения (в контексте)
          newText =
            editContent.substring(0, start - tagLength) +
            selectedText +
            editContent.substring(end + tagLength);
          newStart = start - tagLength;
          newEnd = end - tagLength;
        } else {
          // Теги находятся внутри выделенного текста
          const innerText = selectedText.substring(
            tagLength,
            selectedText.length - tagLength
          );
          newText =
            editContent.substring(0, start) +
            innerText +
            editContent.substring(end);
          newStart = start;
          newEnd = start + innerText.length;
        }
      } else {
        // Проверяем, является ли это форматированием списка или цитаты
        const isListFormatting = /^[-*+]\s|^\d+\.\s|^- \[ \]\s|^>\s/.test(
          before
        );
        const isMultiline = selectedText.includes("\n");

        if (isListFormatting && isMultiline) {
          // Обрабатываем многострочное выделение для списков
          const lines = selectedText.split("\n");
          let processedLines: string[] = [];
          let currentNumber = 1;
          let isFirstNonEmptyLine = true;

          for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            const trimmedLine = line.trim();

            // Пропускаем пустые строки
            if (trimmedLine === "") {
              processedLines.push(line);
              continue;
            }

            // Определяем отступ текущей строки
            const indentMatch = line.match(/^(\s*)/);
            const indent = indentMatch ? indentMatch[1] : "";

            // Обрабатываем в зависимости от типа маркера
            if (before.startsWith("- [ ]")) {
              // Чекбокс
              processedLines.push(indent + "- [ ] " + trimmedLine);
            } else if (before.startsWith("- ")) {
              // Маркированный список
              processedLines.push(indent + "- " + trimmedLine);
            } else if (before.match(/^\d+\.\s/)) {
              // Нумерованный список - извлекаем начальный номер
              const numberMatch = before.match(/^(\d+)\.\s/);
              if (numberMatch && isFirstNonEmptyLine) {
                // Для первой непустой строки используем номер из маркера
                currentNumber = parseInt(numberMatch[1]);
                isFirstNonEmptyLine = false;
              } else if (isFirstNonEmptyLine) {
                // Если маркера нет, начинаем с 1
                currentNumber = 1;
                isFirstNonEmptyLine = false;
              }
              processedLines.push(indent + currentNumber + ". " + trimmedLine);
              currentNumber++;
            } else if (before.startsWith("> ")) {
              // Цитата
              processedLines.push(indent + "> " + trimmedLine);
            } else {
              // Для других форматов просто добавляем маркер
              processedLines.push(indent + before + trimmedLine);
            }
          }

          const processedText = processedLines.join("\n");
          newText =
            editContent.substring(0, start) +
            processedText +
            editContent.substring(end);
          newStart = start + before.length;
          newEnd = start + processedText.length;
        } else {
          // Добавляем теги обычным способом
          newText =
            editContent.substring(0, start) +
            before +
            selectedText +
            after +
            editContent.substring(end);
          newStart = start + before.length;
          newEnd = end + before.length;
        }
      }

      setEditContent(newText);

      // Восстанавливаем фокус и выделение, затем обновляем активные форматы
      setTimeout(() => {
        textarea.focus();
        textarea.setSelectionRange(newStart, newEnd);
        // Обновляем активные форматы после применения форматирования
        const formats = getActiveFormats();
        setActiveFormats(formats);
      }, 0);
    },
    [editContent, getActiveFormats]
  );

  const insertColorMarkdown = useCallback(() => {
    const colorDialog = document.createElement("input");
    colorDialog.type = "color";
    colorDialog.style.display = "none";
    document.body.appendChild(colorDialog);

    colorDialog.addEventListener("change", function () {
      const selectedColor = this.value;
      const textarea = editTextareaRef.current;
      if (!textarea) return;

      const start = textarea.selectionStart;
      const end = textarea.selectionEnd;
      const selected = editContent.substring(start, end);
      const before = editContent.substring(0, start);
      const after = editContent.substring(end);

      let replacement;
      if (selected.trim() === "") {
        replacement = `<span style="color: ${selectedColor}">Текст</span>`;
      } else {
        replacement = `<span style="color: ${selectedColor}">${selected}</span>`;
      }

      const newText = before + replacement + after;
      setEditContent(newText);

      setTimeout(() => {
        textarea.focus();
        const cursorPosition = start + replacement.length;
        textarea.setSelectionRange(cursorPosition, cursorPosition);
      }, 0);

      document.body.removeChild(this);
    });

    colorDialog.addEventListener("cancel", function () {
      document.body.removeChild(this);
    });

    colorDialog.click();
  }, [editContent]);

  // Функция для вычисления позиции курсора в textarea
  const getCursorPosition = useCallback(() => {
    const textarea = editTextareaRef.current;
    if (!textarea) return null;

    const start = textarea.selectionStart;
    const end = textarea.selectionEnd;
    const hasSelection = start !== end;

    // Используем середину выделения или позицию курсора для позиционирования
    const position = hasSelection ? Math.floor((start + end) / 2) : start;
    const text = textarea.value.substring(0, position);
    const lines = text.split("\n");
    const lineNumber = lines.length - 1;
    const currentLineText = lines[lines.length - 1];

    // Получаем размеры textarea
    const rect = textarea.getBoundingClientRect();
    const styles = window.getComputedStyle(textarea);
    const lineHeight = parseInt(styles.lineHeight) || 20;
    const paddingTop = parseInt(styles.paddingTop) || 0;
    const paddingLeft = parseInt(styles.paddingLeft) || 0;
    // @ts-expect-error - переменная может использоваться в будущем
    const _fontSize = parseInt(styles.fontSize) || 14;

    // Более точный расчет ширины символа
    // Создаем временный элемент для измерения
    const measureEl = document.createElement("span");
    measureEl.style.position = "absolute";
    measureEl.style.visibility = "hidden";
    measureEl.style.whiteSpace = "pre";
    measureEl.style.font = styles.font;
    measureEl.textContent = currentLineText;
    document.body.appendChild(measureEl);
    const textWidth = measureEl.offsetWidth;
    document.body.removeChild(measureEl);

    // Вычисляем позицию (середина выделения или позиция курсора)
    const top =
      rect.top + paddingTop + lineNumber * lineHeight + lineHeight / 2;
    const left = rect.left + paddingLeft + textWidth;

    return { top, left, hasSelection };
  }, []);

  // Обработчик выделения текста
  const handleSelection = useCallback(() => {
    if (localPreviewMode || !floatingToolbarEnabled) {
      setShowFloatingToolbar(false);
      return;
    }

    // Проверяем, есть ли текст в редакторе
    const hasText = editContent.trim().length > 0;

    const position = getCursorPosition();
    if (position && hasText) {
      setToolbarPosition({ top: position.top, left: position.left });
      setHasSelection(position.hasSelection);
      setShowFloatingToolbar(true);
      // Определяем активные форматы только если есть выделение
      if (position.hasSelection) {
        const formats = getActiveFormats();
        setActiveFormats(formats);
      } else {
        setActiveFormats({ bold: false, italic: false, strikethrough: false });
      }
    } else {
      setShowFloatingToolbar(false);
      setHasSelection(false);
      setActiveFormats({ bold: false, italic: false, strikethrough: false });
    }
  }, [
    localPreviewMode,
    editContent,
    getCursorPosition,
    getActiveFormats,
    floatingToolbarEnabled,
  ]);

  const handleImageButtonClick = () => {
    imageInputRef.current?.click();
  };

  const handleFileButtonClick = () => {
    fileInputRef.current?.click();
  };

  const handleImageSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    const newImages = files.filter(
      (file) => file.type.startsWith("image/") && file.size <= 10 * 1024 * 1024
    );

    if (newImages.length + editImages.length > 10) {
      showNotification("Можно загрузить максимум 10 изображений", "warning");
      return;
    }

    setEditImages([...editImages, ...newImages]);
    if (imageInputRef.current) {
      imageInputRef.current.value = "";
    }
  };

  const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
    const selectedFiles = Array.from(e.target.files || []);
    const allowedExtensions = /pdf|doc|docx|xls|xlsx|txt|zip|rar|7z/;
    const allowedMimes = [
      "application/pdf",
      "application/msword",
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "application/vnd.ms-excel",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "text/plain",
      "application/zip",
      "application/x-zip-compressed",
      "application/x-rar-compressed",
      "application/x-7z-compressed",
    ];

    const newFiles = selectedFiles.filter(
      (file) =>
        (allowedMimes.includes(file.type) ||
          allowedExtensions.test(
            file.name.split(".").pop()?.toLowerCase() || ""
          )) &&
        file.size <= 50 * 1024 * 1024
    );

    setEditFiles([...editFiles, ...newFiles]);
    if (fileInputRef.current) {
      fileInputRef.current.value = "";
    }
  };

  // Обработчик клавиш для режима редактирования
  const handleEditKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if ((e.altKey || e.ctrlKey) && e.key === "Enter") {
      e.preventDefault();
      handleSaveEdit();
    } else if (e.key === "Escape") {
      e.preventDefault();
      handleCancelEdit();
    } else if (e.key === "Enter") {
      // Автоматическое продолжение списков
      const textarea = e.currentTarget;
      const start = textarea.selectionStart;
      const text = textarea.value;
      const lines = text.split("\n");

      // Определяем текущую строку
      // @ts-expect-error - переменная используется для определения текущей строки
      let _currentLineIndex = 0;
      let currentLineStart = 0;
      let currentLine = "";

      for (let i = 0; i < lines.length; i++) {
        const lineLength = lines[i].length;
        if (currentLineStart + lineLength >= start) {
          _currentLineIndex = i;
          currentLine = lines[i];
          break;
        }
        currentLineStart += lineLength + 1;
      }

      // Проверяем, является ли текущая строка списком
      const listPatterns = [
        /^(\s*)- \[ \] /,
        /^(\s*)- \[x\] /i,
        /^(\s*)- /,
        /^(\s*)\* /,
        /^(\s*)\+ /,
        /^(\s*)(\d+)\. /,
        /^(\s*)(\w+)\. /,
        /^(\s*)1\. /,
      ];

      let listMatch = null;
      let listType = null;

      for (const pattern of listPatterns) {
        const match = currentLine.match(pattern);
        if (match) {
          listMatch = match;
          if (pattern === listPatterns[0] || pattern === listPatterns[1]) {
            listType = "checkbox";
          } else if (
            pattern === listPatterns[2] ||
            pattern === listPatterns[3] ||
            pattern === listPatterns[4]
          ) {
            listType = "unordered";
          } else if (pattern === listPatterns[7]) {
            listType = "numbered";
          } else {
            listType = "ordered";
          }
          break;
        }
      }

      if (listMatch) {
        e.preventDefault();

        const indent = listMatch[1] || "";
        const marker = listMatch[0].slice(indent.length);
        const afterMarker = currentLine.slice(listMatch[0].length);

        if (afterMarker.trim() === "") {
          const beforeCursor = text.substring(0, start);
          const afterCursor = text.substring(start);

          const newBefore = beforeCursor.replace(
            /\n\s*- \[ \] \s*$|\n\s*- \[x\] \s*$|\n\s*[-*+]\s*$|\n\s*\d+\.\s*$|\n\s*\w+\.\s*$/i,
            "\n"
          );
          const newContent = newBefore + afterCursor;
          setEditContent(newContent);

          setTimeout(() => {
            const newCursorPos = newBefore.length;
            textarea.setSelectionRange(newCursorPos, newCursorPos);
          }, 0);
        } else {
          const beforeCursor = text.substring(0, start);
          const afterCursor = text.substring(start);

          let newMarker = "";
          if (listType === "checkbox") {
            newMarker = indent + "- [ ] ";
          } else if (listType === "unordered") {
            newMarker = indent + marker;
          } else if (listType === "ordered") {
            const number = parseInt(listMatch[2]);
            const nextNumber = number + 1;
            const numberStr = listMatch[2].replace(
              /\d+/,
              nextNumber.toString()
            );
            newMarker = indent + numberStr + ". ";
          } else if (listType === "numbered") {
            newMarker = indent + "1. ";
          }

          const newContent = beforeCursor + "\n" + newMarker + afterCursor;
          setEditContent(newContent);

          setTimeout(() => {
            const newCursorPos = start + 1 + newMarker.length;
            textarea.setSelectionRange(newCursorPos, newCursorPos);
          }, 0);
        }
      }
    }
  };

  // Авторасширение textarea в режиме редактирования
  useEffect(() => {
    if (!isEditing) return;

    const textarea = editTextareaRef.current;
    if (!textarea) return;

    const autoExpand = () => {
      textarea.style.height = "auto";
      textarea.style.height = textarea.scrollHeight + "px";
    };

    textarea.addEventListener("input", autoExpand);
    autoExpand();

    return () => {
      textarea.removeEventListener("input", autoExpand);
    };
  }, [isEditing, editContent]);

  // Фокусировка на textarea при переходе в режим редактирования
  useEffect(() => {
    if (isEditing && editTextareaRef.current && !localPreviewMode) {
      setTimeout(() => {
        const textarea = editTextareaRef.current;
        if (textarea) {
          textarea.focus();
          // Устанавливаем курсор в конец текста только при первом входе в режим редактирования
          if (shouldSetCursorToEndRef.current) {
            // Используем актуальное значение из textarea, а не из состояния
            const contentLength = textarea.value.length;
            textarea.setSelectionRange(contentLength, contentLength);
            shouldSetCursorToEndRef.current = false; // Сбрасываем флаг после установки
          }
        }
      }, 100);
    }
  }, [isEditing, localPreviewMode]); // Убрали все зависимости от editContent

  // Отслеживание выделения текста
  useEffect(() => {
    if (!isEditing) return;

    const textarea = editTextareaRef.current;
    if (!textarea || localPreviewMode) return;

    const handleMouseUp = () => {
      setTimeout(handleSelection, 0);
    };

    const handleMouseMove = (e: MouseEvent) => {
      if (e.buttons === 1) {
        // Если зажата левая кнопка мыши (выделение)
        setTimeout(handleSelection, 0);
      }
    };

    const handleKeyUp = () => {
      setTimeout(handleSelection, 0);
    };

    // Предотвращаем появление контекстного меню браузера при выделении текста
    // Но разрешаем его, если редактор пустой (чтобы можно было вставить текст)
    const handleContextMenu = (e: MouseEvent) => {
      const target = e.target as HTMLElement;
      // Проверяем, что событие происходит в textarea
      if (target === textarea || textarea.contains(target)) {
        const hasText = textarea.value.trim().length > 0;
        const hasSelection = textarea.selectionStart !== textarea.selectionEnd;
        // Блокируем браузерное меню только если есть текст И есть выделение
        if (hasText && hasSelection) {
          e.preventDefault();
          e.stopPropagation();
        }
      }
    };

    textarea.addEventListener("mouseup", handleMouseUp);
    textarea.addEventListener("mousemove", handleMouseMove);
    textarea.addEventListener("keyup", handleKeyUp);
    textarea.addEventListener("contextmenu", handleContextMenu);
    document.addEventListener("selectionchange", handleSelection);
    document.addEventListener("contextmenu", handleContextMenu, true);

    return () => {
      textarea.removeEventListener("mouseup", handleMouseUp);
      textarea.removeEventListener("mousemove", handleMouseMove);
      textarea.removeEventListener("keyup", handleKeyUp);
      textarea.removeEventListener("contextmenu", handleContextMenu);
      document.removeEventListener("selectionchange", handleSelection);
      document.removeEventListener("contextmenu", handleContextMenu, true);
    };
  }, [isEditing, isPreviewMode, handleSelection]);

  // Скрываем toolbar при клике вне textarea и toolbar
  useEffect(() => {
    if (!isEditing) return;

    const handleClickOutside = (e: MouseEvent) => {
      const textarea = editTextareaRef.current;
      const target = e.target as Node;

      // Проверяем, не кликнули ли на toolbar или его кнопки
      const floatingToolbar = document.querySelector(".floating-toolbar");
      if (floatingToolbar && floatingToolbar.contains(target)) {
        // Кликнули на toolbar - не скрываем его
        return;
      }

      if (textarea && !textarea.contains(target)) {
        // Кликнули вне textarea и toolbar - скрываем только если нет выделения
        setTimeout(() => {
          if (textarea.selectionStart === textarea.selectionEnd) {
            setShowFloatingToolbar(false);
          }
        }, 0);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [isEditing]);

  // Обновляем позицию toolbar при прокрутке
  useEffect(() => {
    if (!isEditing || !showFloatingToolbar) return;

    const handleScroll = () => {
      const position = getCursorPosition();
      if (position) {
        setToolbarPosition({ top: position.top, left: position.left });
        setHasSelection(position.hasSelection);
        // Обновляем активные форматы при прокрутке только если есть выделение
        if (position.hasSelection) {
          const formats = getActiveFormats();
          setActiveFormats(formats);
        }
      }
    };

    const textarea = editTextareaRef.current;
    if (textarea) {
      textarea.addEventListener("scroll", handleScroll);
      window.addEventListener("scroll", handleScroll, true);
    }

    return () => {
      if (textarea) {
        textarea.removeEventListener("scroll", handleScroll);
      }
      window.removeEventListener("scroll", handleScroll, true);
    };
  }, [isEditing, showFloatingToolbar, getCursorPosition, getActiveFormats]);

  const handleArchiveConfirm = () => {
    setShowArchiveModal(false);
    onArchive(note.id);
  };

  // Форматируем время, убирая лишний ноль в конце (например, "19:420" -> "19:42")
  const fixTime = (time: string | undefined): string => {
    if (!time) return time || "";
    // Обрезаем все лишние цифры после формата HH:mm (первые 5 символов)
    // Например, "19:420" -> "19:42", "19:421" -> "19:42"
    if (time.length > 5 && time.match(/^\d{2}:\d{2}/)) {
      return time.substring(0, 5);
    }
    return time;
  };

  // Форматируем дату
  const formatDate = () => {
    if (note.created_at) {
      const created = parseSQLiteUtc(note.created_at);
      const createdStr = formatLocalDateTime(created);
      // Убеждаемся, что строка не содержит лишних символов
      const cleanCreatedStr = createdStr.replace(
        /(\d{2}\.\d{2}\.\d{4} \d{2}:\d{2})\d*.*/,
        "$1"
      );

      if (note.updated_at && note.created_at !== note.updated_at) {
        const showEditDate =
          user?.show_edit_date !== undefined ? user.show_edit_date === 1 : true;
        const updated = parseSQLiteUtc(note.updated_at);
        const updatedStr = formatLocalDateTime(updated);
        // Убеждаемся, что строка не содержит лишних символов
        const cleanUpdatedStr = updatedStr.replace(
          /(\d{2}\.\d{2}\.\d{4} \d{2}:\d{2})\d*.*/,
          "$1"
        );

        if (showEditDate) {
          return (
            <>
              {cleanCreatedStr}
              <span className="date-separator"> | </span>
              <Icon
                icon="mdi:pencil"
                style={{ fontSize: "12px", margin: "0 2px" }}
              />
              {cleanUpdatedStr}
            </>
          );
        } else {
          return (
            <>
              {cleanCreatedStr}
              <Icon
                icon="mdi:pencil"
                style={{ fontSize: "12px", margin: "0 2px" }}
              />
            </>
          );
        }
      } else {
        return cleanCreatedStr;
      }
    } else {
      const fixedTime = fixTime(note.time);
      return `${note.date} ${fixedTime}`;
    }
  };

  // Форматируем контент
  const formatContent = () => {
    let contentToProcess = note.content;

    if (searchQuery) {
      contentToProcess = highlightSearchText(contentToProcess, searchQuery);
    }

    const contentWithClickableTags = makeTagsClickable(contentToProcess);
    return parseMarkdown(contentWithClickableTags);
  };

  const getFileIcon = (filename: string): string => {
    const ext = filename.split(".").pop()?.toLowerCase() || "";
    if (ext === "pdf") return "mdi:file-pdf";
    if (["doc", "docx"].includes(ext)) return "mdi:file-word";
    if (["xls", "xlsx"].includes(ext)) return "mdi:file-excel";
    if (ext === "txt") return "mdi:file-document";
    if (["zip", "rar", "7z"].includes(ext)) return "mdi:folder-zip";
    return "mdi:file";
  };

  const formatFileSize = (bytes: number): string => {
    return (bytes / 1024 / 1024).toFixed(2) + " MB";
  };

  const handleImageClick = (imagePath: string) => {
    const modal = document.getElementById("imageModal");
    const modalImage = document.getElementById("modalImage");
    if (modal && modalImage) {
      modalImage.setAttribute("src", imagePath);
      modal.style.display = "block";
    }
  };

  const handleTagClick = (e: React.MouseEvent, tag: string) => {
    e.stopPropagation();
    // Всегда сохраняем тег в нижнем регистре для единообразия
    dispatch(setSelectedTag(tag.toLowerCase()));
  };

  const toggleExpand = () => {
    setIsExpanded(!isExpanded);
  };

  // Проверка, является ли заметка длинной
  useEffect(() => {
    if (isEditing) {
      setIsExpanded(false);
      setIsLongNote(false);
      return;
    }

    if (!textNoteRef.current) return;

    const checkNoteLength = () => {
      const element = textNoteRef.current;
      if (!element) return;

      // Временно убираем класс collapsed для точного измерения
      const wasCollapsed = element.classList.contains("collapsed");
      if (wasCollapsed) {
        element.classList.remove("collapsed");
      }

      // Получаем реальную высоту контента
      const scrollHeight = element.scrollHeight;

      // Восстанавливаем класс если он был
      if (wasCollapsed && !isExpanded) {
        element.classList.add("collapsed");
      }

      // Считаем заметку длинной, если она больше 300px (максимальная высота свернутой заметки)
      const isLong = scrollHeight > 300;
      setIsLongNote(isLong);
    };

    // Небольшая задержка для того, чтобы контент успел отрендериться
    const timer = setTimeout(checkNoteLength, 100);

    return () => clearTimeout(timer);
  }, [note.content, isEditing, isExpanded]);

  // Сбрасываем состояние раскрытия при изменении заметки
  useEffect(() => {
    setIsExpanded(false);
  }, [note.id]);

  return (
    <>
      <div
        className={`container ${note.is_pinned ? "note-pinned" : ""}`}
        data-note-id={note.id}
      >
        <div className="date">
          <span className="date-text">
            {formatDate()}
            {note.is_pinned ? (
              <span className="pin-indicator">
                <Icon icon="mdi:pin" />
                Закреплено
              </span>
            ) : null}
            {note.syncStatus === "pending" && (
              <span className="sync-indicator" title="Ожидает синхронизации">
                <Icon icon="mdi:cloud-upload" />
              </span>
            )}
            {note.syncStatus === "error" && (
              <span
                className="sync-error-indicator"
                title="Ошибка синхронизации"
              >
                <Icon icon="mdi:cloud-alert" />
              </span>
            )}
          </span>
          <div className="note-actions">
            <div
              className="notesHeaderBtn"
              onClick={() => onPin(note.id)}
              title={note.is_pinned ? "Открепить" : "Закрепить"}
            >
              <Icon icon={note.is_pinned ? "mdi:pin-off" : "mdi:pin"} />
            </div>
            <div
              className="notesHeaderBtn"
              onClick={handleEdit}
              title="Редактировать"
            >
              <Icon icon="mdi:pencil" />
            </div>
            <input
              type="checkbox"
              checked={isSelected}
              onChange={() => onSelect && onSelect(note.id)}
              onClick={(e) => e.stopPropagation()}
            />
            {/* <div
              className="notesHeaderBtn"
              onClick={handleArchiveClick}
              title="В архив"
            >
              <Icon icon="mdi:delete" />
            </div> */}
          </div>
        </div>

        {isEditing ? (
          <div className="note-edit-mode">
            <MarkdownToolbar
              onInsert={insertMarkdown}
              onImageClick={handleImageButtonClick}
              onFileClick={handleFileButtonClick}
              onPreviewToggle={toggleLocalPreviewMode}
              isPreviewMode={localPreviewMode}
            />

            <input
              ref={imageInputRef}
              type="file"
              id="imageInput"
              accept="image/*"
              multiple
              style={{ display: "none" }}
              onChange={handleImageSelect}
            />

            <input
              ref={fileInputRef}
              type="file"
              id="fileInput"
              accept=".pdf,.doc,.docx,.xls,.xlsx,.txt,.zip,.rar,.7z"
              multiple
              style={{ display: "none" }}
              onChange={handleFileSelect}
            />

            {!localPreviewMode && (
              <>
                <textarea
                  ref={editTextareaRef}
                  className="textInput"
                  value={editContent}
                  onChange={(e) => setEditContent(e.target.value)}
                  onKeyDown={handleEditKeyDown}
                  style={{ minHeight: "100px" }}
                  onContextMenu={(e) => {
                    // Предотвращаем появление контекстного меню браузера при выделении текста
                    // Но разрешаем его, если редактор пустой (чтобы можно было вставить текст)
                    const textarea = editTextareaRef.current;
                    if (textarea) {
                      const hasText = textarea.value.trim().length > 0;
                      const hasSelection =
                        textarea.selectionStart !== textarea.selectionEnd;
                      // Блокируем браузерное меню только если есть текст И есть выделение
                      if (hasText && hasSelection) {
                        e.preventDefault();
                      }
                    }
                  }}
                />
                {floatingToolbarEnabled && (
                  <FloatingToolbar
                    textareaRef={editTextareaRef}
                    onFormat={insertMarkdown}
                    visible={showFloatingToolbar}
                    position={toolbarPosition}
                    onHide={() => setShowFloatingToolbar(false)}
                    onInsertColor={insertColorMarkdown}
                    activeFormats={activeFormats}
                    hasSelection={hasSelection}
                  />
                )}
              </>
            )}

            {localPreviewMode && <NotePreview content={editContent} />}

            {/* Существующие изображения */}
            {note.images && note.images.length > 0 && (
              <div
                className="image-preview-container"
                style={{ display: "block" }}
              >
                <div className="image-preview-header">
                  <span>Прикрепленные изображения:</span>
                </div>
                <div className="image-preview-list">
                  {note.images
                    .filter((image) => !deletedImageIds.includes(image.id))
                    .map((image) => {
                      const imageUrl = getImageUrl(
                        image.file_path,
                        Number(note.id),
                        Number(image.id)
                      );
                      return (
                        <div key={image.id} className="image-preview-item">
                          <img
                            src={imageUrl}
                            alt={image.original_name}
                            className="image-preview-thumbnail"
                          />
                          <button
                            className="image-preview-remove"
                            onClick={() => handleDeleteExistingImage(image.id)}
                            title="Удалить"
                          >
                            <Icon icon="mdi:close" />
                          </button>
                        </div>
                      );
                    })}
                </div>
              </div>
            )}

            {/* Удаленные изображения (для возможности восстановления) */}
            {deletedImageIds.length > 0 && (
              <div
                className="image-preview-container"
                style={{ display: "block", opacity: 0.5 }}
              >
                <div className="image-preview-header">
                  <span>Изображения для удаления:</span>
                </div>
                <div className="image-preview-list">
                  {note.images
                    .filter((image) => deletedImageIds.includes(image.id))
                    .map((image) => {
                      const imageUrl = getImageUrl(
                        image.file_path,
                        Number(note.id),
                        Number(image.id)
                      );
                      return (
                        <div key={image.id} className="image-preview-item">
                          <img
                            src={imageUrl}
                            alt={image.original_name}
                            className="image-preview-thumbnail"
                            style={{ opacity: 0.5 }}
                          />
                          <button
                            className="image-preview-remove restore-btn"
                            onClick={() => handleRestoreImage(image.id)}
                            title="Восстановить"
                          >
                            <Icon icon="mdi:restore" />
                          </button>
                        </div>
                      );
                    })}
                </div>
              </div>
            )}

            {/* Существующие файлы */}
            {note.files && note.files.length > 0 && (
              <div
                className="file-preview-container"
                style={{ display: "block" }}
              >
                <div className="file-preview-header">
                  <span>Прикрепленные файлы:</span>
                </div>
                <div className="file-preview-list">
                  {note.files
                    .filter((file) => !deletedFileIds.includes(file.id))
                    .map((file) => {
                      // fileUrl не используется в этом контексте
                      return (
                        <div key={file.id} className="file-preview-item">
                          <Icon
                            icon={getFileIcon(file.original_name)}
                            className="file-icon"
                          />
                          <div className="file-info">
                            <div className="file-name">
                              {file.original_name}
                            </div>
                            <div className="file-size">
                              {formatFileSize(file.file_size)}
                            </div>
                          </div>
                          <button
                            className="file-preview-remove"
                            onClick={() => handleDeleteExistingFile(file.id)}
                            title="Удалить"
                          >
                            <Icon icon="mdi:close" />
                          </button>
                        </div>
                      );
                    })}
                </div>
              </div>
            )}

            {/* Удаленные файлы (для возможности восстановления) */}
            {deletedFileIds.length > 0 && (
              <div
                className="file-preview-container"
                style={{ display: "block", opacity: 0.5 }}
              >
                <div className="file-preview-header">
                  <span>Файлы для удаления:</span>
                </div>
                <div className="file-preview-list">
                  {note.files
                    .filter((file) => deletedFileIds.includes(file.id))
                    .map((file) => {
                      return (
                        <div key={file.id} className="file-preview-item">
                          <Icon
                            icon={getFileIcon(file.original_name)}
                            className="file-icon"
                            style={{ opacity: 0.5 }}
                          />
                          <div className="file-info">
                            <div className="file-name" style={{ opacity: 0.5 }}>
                              {file.original_name}
                            </div>
                            <div className="file-size" style={{ opacity: 0.5 }}>
                              {formatFileSize(file.file_size)}
                            </div>
                          </div>
                          <button
                            className="file-preview-remove restore-btn"
                            onClick={() => handleRestoreFile(file.id)}
                            title="Восстановить"
                          >
                            <Icon icon="mdi:restore" />
                          </button>
                        </div>
                      );
                    })}
                </div>
              </div>
            )}

            <ImageUpload images={editImages} onChange={setEditImages} />
            <FileUpload files={editFiles} onChange={setEditFiles} />

            {user?.is_public_profile === 1 && (
              <div
                className="privacy-toggle-container"
                style={{
                  marginBottom: "10px",
                  display: "flex",
                  alignItems: "center",
                  gap: "10px",
                }}
              >
                <label
                  style={{
                    display: "flex",
                    alignItems: "center",
                    gap: "8px",
                    cursor: "pointer",
                  }}
                >
                  <input
                    type="checkbox"
                    checked={editIsPrivate}
                    onChange={(e) => setEditIsPrivate(e.target.checked)}
                    style={{
                      width: "18px",
                      height: "18px",
                      cursor: "pointer",
                      accentColor: "var(--accent-color, #007bff)",
                    }}
                  />
                  <span
                    style={{
                      fontSize: "14px",
                      color: "var(--text-primary, #333)",
                    }}
                  >
                    Приватная заметка
                  </span>
                </label>
              </div>
            )}

            <div className="save-button-container">
              <div className="action-buttons">
                {aiEnabled && (
                  <>
                    <button
                      className="btnSave btnAI"
                      onClick={handleAiImprove}
                      disabled={isAiLoading}
                      title="Улучшить или создать текст через ИИ"
                    >
                      <Icon icon="mdi:robot" />
                      <span className="btnAI-text">
                        {isAiLoading ? "Обработка..." : "Помощь ИИ"}
                      </span>
                    </button>
                    <button
                      className="btnSave btnAI"
                      onClick={handleGenerateTags}
                      disabled={isGeneratingTags || isAiLoading}
                      title="Сгенерировать теги через ИИ"
                    >
                      <Icon icon="mdi:tag-multiple" />
                      <span className="btnAI-text">
                        {isGeneratingTags ? "Генерация..." : "Теги ИИ"}
                      </span>
                    </button>
                  </>
                )}
                <button className="btnSave" onClick={handleSaveEdit}>
                  Сохранить
                </button>
                <button className="btn-secondary" onClick={handleCancelEdit}>
                  Отмена
                </button>
              </div>
              <span className="save-hint">
                Alt + Enter для сохранения, Esc для отмены
              </span>
            </div>
          </div>
        ) : (
          <>
            <div
              ref={textNoteRef}
              className={`textNote ${
                isLongNote && !isExpanded ? "collapsed" : ""
              }`}
              data-original-content={note.content}
              dangerouslySetInnerHTML={{ __html: formatContent() }}
              onClick={(e) => {
                const target = e.target as HTMLElement;
                if (target.classList.contains("tag-in-note")) {
                  const tag = target.getAttribute("data-tag");
                  if (tag) {
                    handleTagClick(e, tag);
                  }
                }
              }}
            />
            {isLongNote && (
              <button
                className="show-more-btn"
                onClick={toggleExpand}
                type="button"
              >
                <Icon
                  icon={isExpanded ? "mdi:chevron-up" : "mdi:chevron-down"}
                />
                <span>{isExpanded ? "Скрыть" : "Раскрыть"}</span>
              </button>
            )}

            {note.images && note.images.length > 0 && (
              <div className="note-images-container">
                {note.images.map((image) => {
                  const imageUrl = getImageUrl(
                    image.file_path,
                    Number(note.id),
                    Number(image.id)
                  );
                  return (
                    <div key={image.id} className="note-image-item">
                      <img
                        src={imageUrl}
                        alt={image.original_name}
                        className="note-image lazy"
                        data-src={imageUrl}
                        data-image-id={image.id}
                        loading="lazy"
                        onClick={() => handleImageClick(imageUrl)}
                      />
                    </div>
                  );
                })}
              </div>
            )}

            {note.files && note.files.length > 0 && (
              <div className="note-files-container">
                {note.files.map((file) => {
                  const fileUrl = getFileUrl(
                    file.file_path,
                    Number(note.id),
                    Number(file.id)
                  );
                  return (
                    <div key={file.id} className="note-file-item">
                      <a
                        href={fileUrl}
                        download={file.original_name}
                        className="note-file-link"
                        data-file-id={file.id}
                      >
                        <Icon
                          icon={getFileIcon(file.original_name)}
                          className="file-icon"
                        />
                        <div className="file-info">
                          <div className="file-name">{file.original_name}</div>
                          <div className="file-size">
                            {formatFileSize(file.file_size)}
                          </div>
                        </div>
                      </a>
                    </div>
                  );
                })}
              </div>
            )}
          </>
        )}
      </div>

      <Modal
        isOpen={showArchiveModal}
        onClose={() => setShowArchiveModal(false)}
        onConfirm={handleArchiveConfirm}
        title="Подтверждение архивирования"
        message="Архивировать эту заметку? Её можно будет восстановить из настроек."
        confirmText="Архивировать"
        cancelText="Отмена"
      />

      <GenerateTagsModal
        isOpen={showTagsModal}
        onClose={() => {
          setShowTagsModal(false);
          setSuggestedTags([]);
          setTagsGenerationError(false);
          setIsGeneratingTags(false);
        }}
        onSelectTags={handleSelectTags}
        suggestedTags={suggestedTags}
        existingTags={extractTags(editContent)}
        isLoading={isGeneratingTags}
        hasError={tagsGenerationError}
      />

      <ImproveTextModal
        isOpen={showImproveModal}
        onClose={() => {
          setShowImproveModal(false);
          setImprovedText("");
          setImproveError(false);
          setImproveErrorMessage("");
          setIsAiLoading(false);
        }}
        onApply={handleApplyImprovedText}
        originalText={editContent}
        improvedText={improvedText}
        isLoading={isAiLoading}
        hasError={improveError}
        errorMessage={improveErrorMessage}
      />
    </>
  );
};
