// src/client/entities/note.ts
// Note entity - Pinia store and types
// Day.js Protocol: Frontend uses dayjs.utc() for comparison, local display for UI

import { defineStore } from 'pinia';
import { useUserStore } from './user';
import { useNotificationStore } from './notification';
import { noteRepository, type Note, type NoteComment, type NoteHistoryEntry, type CreateNoteInput, type UpdateNoteInput, type ChangeNoteStatusInput, type CreateNoteCommentInput } from '../shared/api/repositories';
import type { NoteLayout, NoteHistoryAction, NoteStatus, NotePriority } from '@shared/contracts/notes';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

// Day.js Protocol: Initialize plugins
dayjs.extend(utc);
dayjs.extend(timezone);

// Re-export shared contracts for convenience
export type {
  NotePriority,
  NoteStatus,
  NoteHistoryAction,
  NoteLayout,
} from '@shared/contracts/notes';

// Re-export types for convenience
export type { Note, NoteComment, NoteHistoryEntry, CreateNoteInput, UpdateNoteInput, ChangeNoteStatusInput, CreateNoteCommentInput };

// Note store
export const useNoteStore = defineStore('note', {
  state: (): {
    notes: Note[];
    comments: NoteComment[];
    history: NoteHistoryEntry[];
    loading: boolean;
    error: string | null;
    currentFilter: 'active' | 'completed' | 'cancelled';
    viewMode: 'grid' | 'list';
    preferredViewMode: 'grid' | 'list';
    authorFilter: string | null;
    includeReminders: boolean;
    visibilityFilter: 'all' | 'private' | 'public';
    sortOrder: 'date-desc' | 'date-asc' | 'title';
  } => ({
    notes: [],
    comments: [],
    history: [],
    loading: false,
    error: null,
    currentFilter: 'active',
    viewMode: 'grid',
    preferredViewMode: 'grid',
    authorFilter: null,
    includeReminders: false,
    visibilityFilter: 'all',
    sortOrder: 'date-desc',
  }),

  getters: {
    // Filtered notes based on currentFilter, author, visibility, and reminders
    // Day.js Protocol: Frontend uses dayjs.utc() for date comparison
    filteredNotes: (state) => {
      const statusMap: Record<string, string> = {
        'active': 'active',
        'completed': 'done',
        'cancelled': 'cancelled',
      };
      const targetStatus = statusMap[state.currentFilter];

      const filtered = state.notes.filter((n) => {
        // Skip archived notes
        if (n.archivedAt) return false;

        // Filter by status
        if (n.status !== targetStatus) return false;

        // Check if note has a reminder
        const hasReminder = !!n.layout?.reminderAt;

        // Rule: If includeReminders === true, note with reminder is always visible (even if author filter doesn't match)
        if (state.includeReminders && hasReminder) {
          // Still apply visibility filter
          if (state.visibilityFilter === 'private' && n.isPublic) return false;
          if (state.visibilityFilter === 'public' && !n.isPublic) return false;
          return true;
        }

        // Filter by author
        if (state.authorFilter && n.authorId !== state.authorFilter) return false;

        // Filter by visibility
        if (state.visibilityFilter === 'private' && n.isPublic) return false;
        if (state.visibilityFilter === 'public' && !n.isPublic) return false;

        return true;
      });

      // Dynamic sorting based on sortOrder
      // Day.js Protocol: Используем dayjs.utc() для корректного сравнения UTC дат
      return filtered.sort((a, b) => {
        if (state.sortOrder === 'date-desc') return dayjs.utc(b.createdAt).valueOf() - dayjs.utc(a.createdAt).valueOf();
        if (state.sortOrder === 'date-asc') return dayjs.utc(a.createdAt).valueOf() - dayjs.utc(b.createdAt).valueOf();
        if (state.sortOrder === 'title') return a.title.localeCompare(b.title);
        return 0;
      });
    },
    activeNotes: (state) => {
      const filtered = state.notes.filter((n) => n.status === 'active' && !n.archivedAt);
      console.log('activeNotes getter: total notes =', state.notes.length, ', active notes =', filtered.length);
      if (filtered.length === 0 && state.notes.length > 0) {
        console.log('activeNotes getter: sample notes:', state.notes.slice(0, 3).map(n => ({
          id: n.id,
          title: n.title,
          status: n.status,
          archivedAt: n.archivedAt,
          authorId: n.authorId,
          isPublic: n.isPublic
        })));
      }
      return filtered;
    },
    archivedNotes: (state) => state.notes.filter((n) => n.archivedAt),
    myNotes: (state) => {
      const userStore = useUserStore();
      const currentUserId = userStore.user?.id;
      return state.notes.filter((n) => n.authorId === currentUserId);
    },
    publicNotes: (state) => state.notes.filter((n) => n.isPublic && !n.archivedAt),
    // Проверка: есть ли непрочитанные комментарии (lastCommentAt > lastViewedAt)
    // Rule 3.I.6 (Author Guard): Не показываем "новое", если комментарий от текущего пользователя
    // Day.js Protocol: Frontend uses dayjs.utc() for comparison
    hasUnreadComments: (state) => (noteId: string) => {
      const note = state.notes.find(n => n.id === noteId);
      if (!note || !note.lastCommentAt) return false;

      const userStore = useUserStore();
      const currentUserId = userStore.user?.id;
      if (!currentUserId) return false;

      const lastViewedAt = note.layout?.lastViewedAt;
      // Day.js Protocol: Используем dayjs.utc() для корректного сравнения UTC дат
      const commentTime = dayjs.utc(note.lastCommentAt);
      const viewedTime = lastViewedAt ? dayjs.utc(lastViewedAt) : dayjs.utc(0);
      const isUnread = commentTime.isAfter(viewedTime) && note.lastCommentAuthorId !== currentUserId;

      console.log(`[DEBUG UNREAD COMMENTS] Note: ${note.title}`, {
        lastCommentAt: note.lastCommentAt,
        lastViewed: lastViewedAt,
        lastCommentAuthorId: note.lastCommentAuthorId,
        currentUserId,
        isUnread
      });

      return isUnread;
    },
    // Проверка: есть ли непрочитанное упоминание текущего пользователя
    // Rule 3.I.6 (Author Guard): Не показываем "новое", если упоминание от текущего пользователя
    // Day.js Protocol: Frontend uses dayjs.utc() for comparison
    hasUserMention: (state) => (noteId: string) => {
      const note = state.notes.find(n => n.id === noteId);
      if (!note || !note.lastMentionAt) return false;

      const userStore = useUserStore();
      const currentUserId = userStore.user?.id;
      if (!currentUserId) return false;

      // Author Guard: Если мы сами себя упомянули — не показываем "новое"
      // (хотя это редкий случай, так как обычно упоминание делает другой пользователь)
      if (note.lastCommentAuthorId === currentUserId) return false;

      const lastViewedAt = note.layout?.lastViewedAt;
      // Day.js Protocol: Используем dayjs.utc() для корректного сравнения UTC дат
      const mentionTime = dayjs.utc(note.lastMentionAt);
      const viewedTime = lastViewedAt ? dayjs.utc(lastViewedAt) : dayjs.utc(0);

      // Сравниваем чистые UTC таймстемпы
      return mentionTime.isAfter(viewedTime);
    },
    // Общий флаг пульсации (Pulse) - срабатывает если ЛЮБОЕ из системных времен больше lastViewedAt
    // Условия:
    // 1. (lastCommentAt > lastViewedAt && lastCommentAuthorId !== currentUserId) - новые комментарии
    // 2. (lastMentionAt > lastViewedAt) - новые упоминания
    // 3. (priorityUpdatedAt > lastViewedAt && lastPriorityAuthorId !== currentUserId) - новый приоритет
    // updatedAt используется только для определения "Новой" заметки (где lastViewedAt === null)
    // Rule 3.I.6 (Author Guard): Не показываем "новое" для действий текущего пользователя
    // Day.js Protocol: Frontend uses dayjs.utc() for comparison
    hasUpdates: (state) => (noteId: string) => {
      const note = state.notes.find(n => n.id === noteId);
      const userStore = useUserStore();
      const currentUserId = userStore.user?.id;
      if (!note || !currentUserId) return false;

      const lastViewedAt = note.layout?.lastViewedAt;
      // Day.js Protocol: Используем dayjs.utc() для корректного сравнения UTC дат
      const viewedTime = lastViewedAt ? dayjs.utc(lastViewedAt) : dayjs.utc(0);
      const commentTime = dayjs.utc(note.lastCommentAt || 0);
      const mentionTime = dayjs.utc(note.lastMentionAt || 0);
      const priorityTime = dayjs.utc(note.priorityUpdatedAt || 0);

      // Проверка 1: есть новые комментарии И не от текущего пользователя
      const hasNewComments = commentTime.isAfter(viewedTime) && note.lastCommentAuthorId !== currentUserId;

      // Проверка 2: есть новые упоминания
      const hasNewMention = mentionTime.isAfter(viewedTime);

      // Проверка 3: приоритет изменен после просмотра И не текущим пользователем
      const hasNewPriority = priorityTime.isAfter(viewedTime) && note.lastPriorityAuthorId !== currentUserId;

      const hasUpdates = hasNewComments || hasNewMention || hasNewPriority;

      console.log(`[DEBUG UPDATES] Note: ${note.title}`, {
        lastViewed: lastViewedAt,
        lastCommentAt: note.lastCommentAt,
        lastMentionAt: note.lastMentionAt,
        priorityUpdatedAt: note.priorityUpdatedAt,
        hasNewComments,
        hasNewMention,
        hasNewPriority,
        hasUpdates
      });

      return hasUpdates;
    },
    // Unified activity feed: combines history and comments into single stream
    combinedActivities: (state) => {
      const getStatusLabel = (status: NoteStatus): string => {
        const statusMap: Record<string, string> = {
          'active': 'Активно',
          'done': 'Выполнено',
          'cancelled': 'Отменено',
        };
        return statusMap[status] || status;
      };

      const getPriorityLabel = (priority: NotePriority): string => {
        const priorityMap: Record<string, string> = {
          'low': 'Низкий',
          'normal': 'Обычный',
          'high': 'Высокий',
        };
        return priorityMap[priority] || priority;
      };

      // Ультимативный маппинг текста истории
      const getHistoryText = (entry: NoteHistoryEntry): string => {
        const action = String(entry.action).trim();
        
        // 1. Сначала обрабатываем наш специфичный случай с приоритетом
        if (action === 'priority_changed') {
          try {
            const payload = typeof entry.payload === 'string' ? JSON.parse(entry.payload) : entry.payload;
            const newP = payload?.changes?.newPriority;
            
            if (newP === 'high') return 'Заметка получила высокий приоритет';
            if (newP === 'normal') return 'Заметка получила обычный приоритет';
            return 'Заметка изменила приоритет';
          } catch (e) {
            return 'Заметка изменила приоритет';
          }
        }

        // 2. Стандартный маппинг для остальных экшенов
        const actionMap: Record<string, string> = {
          'created': 'Заметка создана',
          'updated': 'Заметка обновлена',
          'status_changed': 'Статус изменен',
          'returned': 'Заметка возвращена',
          'commented': 'Добавлен комментарий',
          'priority_changed': 'Приоритет изменен'
        };

        let text = actionMap[action] || action;

        // Добавляем детали для смены статуса
        if (action === 'status_changed' && entry.payload) {
          const payload = typeof entry.payload === 'string' ? JSON.parse(entry.payload) : entry.payload;
          if (payload.commentText) return payload.commentText;
          
          const oldStatus = getStatusLabel(payload.oldStatus as NoteStatus);
          const newStatus = getStatusLabel(payload.newStatus as NoteStatus);
          text += `: ${oldStatus} → ${newStatus}`;
        }

        return text;
      };

      // Маппинг истории
      const historyActivities = state.history
        .filter(entry => entry.action !== 'commented' && entry.action !== 'updated')
        .map(entry => {
          const action = String(entry.action).trim();
          const text = getHistoryText(entry);
          let systemInfo: string | undefined;

          // Формируем системную информацию для тултипа (popper)
          if (action === 'status_changed' || action === 'priority_changed') {
            try {
              const payload = typeof entry.payload === 'string' ? JSON.parse(entry.payload) : entry.payload;
              
              if (action === 'status_changed') {
                const oldS = getStatusLabel(payload.oldStatus);
                const newS = getStatusLabel(payload.newStatus);
                systemInfo = `Статус: ${oldS} → ${newS}`;
              } else {
                const oldP = getPriorityLabel(payload?.changes?.oldPriority);
                const newP = getPriorityLabel(payload?.changes?.newPriority);
                systemInfo = `Приоритет: ${oldP} → ${newP}`;
              }
            } catch (e) {
              systemInfo = text;
            }
          }

          return {
            id: entry.id,
            activityType: action as any,
            authorId: entry.authorId,
            createdAt: entry.createdAt,
            text,
            payload: entry.payload,
            systemInfo,
          };
        });

      const commentActivities = state.comments.map(comment => ({
        id: comment.id,
        activityType: 'commented' as const,
        authorId: comment.authorId,
        createdAt: comment.createdAt,
        text: comment.content,
      }));

      // Day.js Protocol: Используем dayjs.utc() для корректного сравнения UTC дат
      return [...historyActivities, ...commentActivities]
        .sort((a, b) => dayjs.utc(b.createdAt).valueOf() - dayjs.utc(a.createdAt).valueOf());
    },
  },

  actions: {
    async fetchNotes(): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        console.log('[NoteStore] fetchNotes: Starting fetch...');
        const response = await noteRepository.getAll();
        console.log('[NoteStore] fetchNotes: received notes count:', response.length);
        console.log('[NoteStore] fetchNotes: sample notes:', response.slice(0, 3).map(n => ({
          id: n.id,
          title: n.title,
          status: n.status,
          authorId: n.authorId,
          userId: n.userId,
          isPublic: n.isPublic
        })));
        this.notes = response;
        console.log('[NoteStore] fetchNotes: activeNotes count:', this.activeNotes.length);
        console.log('[NoteStore] fetchNotes: Fetch completed successfully');
      } catch (error: any) {
        console.error('[NoteStore] fetchNotes: Error details:', error);
        console.error('[NoteStore] fetchNotes: Error message:', error.message);
        this.error = error.message || 'Failed to fetch notes';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async fetchNotesFresh(): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        console.log('[NoteStore] fetchNotesFresh: Starting fresh fetch (bypassing cache)...');
        const response = await noteRepository.getAllFresh();
        console.log('[NoteStore] fetchNotesFresh: received notes count:', response.length);
        console.log('[NoteStore] fetchNotesFresh: sample notes:', response.slice(0, 3).map(n => ({
          id: n.id,
          title: n.title,
          status: n.status,
          authorId: n.authorId,
          userId: n.userId,
          isPublic: n.isPublic
        })));
        this.notes = response;
        console.log('[NoteStore] fetchNotesFresh: activeNotes count:', this.activeNotes.length);
        console.log('[NoteStore] fetchNotesFresh: Fresh fetch completed successfully');
      } catch (error: any) {
        console.error('[NoteStore] fetchNotesFresh: Error details:', error);
        console.error('[NoteStore] fetchNotesFresh: Error message:', error.message);
        this.error = error.message || 'Failed to fetch notes';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async createNote(input: CreateNoteInput): Promise<Note> {
      this.loading = true;
      this.error = null;
      try {
        console.log('createNote: Creating note with input:', input);
        const response = await noteRepository.create(input);
        this.notes.push(response);
        console.log('createNote: Successfully created note:', response);
        return response;
      } catch (error: any) {
        console.error('createNote: Error details:', error);
        console.error('createNote: Error message:', error.message);
        this.error = error.message || 'Failed to create note';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async updateNote(id: string, updates: UpdateNoteInput): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        console.log('[NoteStore] updateNote: id=' + id + ', updates=', updates);
        const response = await noteRepository.update(id, updates);
        console.log('[NoteStore] updateNote: response=', response);
        const index = this.notes.findIndex((n) => n.id === id);
        if (index !== -1) {
          console.log('[NoteStore] updateNote: updating note at index=' + index);
          console.log('[NoteStore] updateNote: before update - note.status=', this.notes[index].status);
          this.notes[index] = response;
          console.log('[NoteStore] updateNote: after update - note.status=', this.notes[index].status);
          console.log('[NoteStore] updateNote: activeNotes count after update=', this.activeNotes.length);
        }
      } catch (error: any) {
        console.error('[NoteStore] updateNote: Error details:', error);
        console.error('[NoteStore] updateNote: Error message:', error.message);
        this.error = error.message || 'Failed to update note';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async changeNoteStatus(id: string, input: ChangeNoteStatusInput): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        const response = await noteRepository.changeStatus(id, input);
        const index = this.notes.findIndex((n) => n.id === id);
        if (index !== -1) {
          this.notes[index] = { ...this.notes[index], ...response };
        }
      } catch (error: any) {
        this.error = error.message || 'Failed to change note status';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async deleteNote(id: string): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        console.log('deleteNote: Deleting note with id:', id);
        await noteRepository.deleteNote(id);
        this.notes = this.notes.filter((n) => n.id !== id);
        console.log('deleteNote: Successfully deleted note with id:', id);
      } catch (error: any) {
        console.error('deleteNote: Error details:', error);
        console.error('deleteNote: Error message:', error.message);
        this.error = error.message || 'Failed to delete note';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async fetchComments(noteId: string): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        const response = await noteRepository.fetchComments(noteId);
        
        // Просто загружаем комментарии без isRead
        this.comments = response;
        
        // Синхронизируем статус с БД
        await this.markAsRead(noteId);
      } catch (error: any) {
        this.error = error.message || 'Failed to fetch comments';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async createComment(input: CreateNoteCommentInput): Promise<NoteComment> {
      this.loading = true;
      this.error = null;
      try {
        console.log('[STORE] createComment called with input:', input);
        const response = await noteRepository.createComment(input);
        console.log('[STORE] createComment response:', response);
        console.log('[STORE] response.createdAt type:', typeof response.createdAt);
        console.log('[STORE] response.createdAt value:', response.createdAt);
        
        // Replace entire array to guarantee Vue 3 reactivity
        this.comments = [...this.comments, response];
        console.log('[STORE] Comments array updated, new length:', this.comments.length);
        
        // Update the note object to maintain reactivity and sync with backend
        const noteIndex = this.notes.findIndex((n) => n.id === input.noteId);
        console.log('[STORE] Found note at index:', noteIndex);
        if (noteIndex !== -1) {
          console.log('[STORE] Before update - note.lastCommentAt:', this.notes[noteIndex].lastCommentAt);
          console.log('[STORE] Before update - note.layout:', this.notes[noteIndex].layout);
          
          // Create a new note object to ensure Vue reactivity
          const updatedNote = { ...this.notes[noteIndex] };
          
          // 1. Update the date of the last comment in the note itself
          updatedNote.lastCommentAt = response.createdAt;
          
          // 2. FIXED: Don't update lastViewedAt here - it should only be updated when modal opens
          // This was causing the indicator to not work properly
          if (updatedNote.layout) {
            updatedNote.layout = {
              ...updatedNote.layout,
              // REMOVED: lastViewedAt: response.createdAt,
            };
          } else {
            // Create layout object if it doesn't exist
            updatedNote.layout = {
              reminderAt: null,
              lastViewedAt: null, // FIXED: Start as null, will be set when modal opens
            };
          }
          
          // Replace the entire note object to ensure Vue reactivity
          this.notes[noteIndex] = updatedNote;
          
          console.log('[STORE] After update - note.lastCommentAt:', this.notes[noteIndex].lastCommentAt);
          console.log('[STORE] After update - note.layout:', this.notes[noteIndex].layout);
          console.log('[STORE] Note object replaced for reactivity - lastCommentViewedAt NOT updated on comment creation');
        } else {
          console.log('[STORE] Note not found in store for ID:', input.noteId);
        }
        
        return response;
      } catch (error: any) {
        console.error('[STORE] createComment error:', error);
        this.error = error.message || 'Failed to create comment';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    async fetchHistory(noteId: string): Promise<void> {
      this.loading = true;
      this.error = null;
      try {
        const response = await noteRepository.fetchHistory(noteId);
        this.history = response;

        // АВТО-СБРОС: Чтение истории тоже считается просмотром заметки
        await this.markAsRead(noteId);
      } catch (error: any) {
        this.error = error.message || 'Failed to fetch history';
        throw error;
      } finally {
        this.loading = false;
      }
    },

    // Local actions
    addNote(note: Note) {
      this.notes.push(note);
    },

    updateLocalNote(id: string, updates: Partial<Note>) {
      const index = this.notes.findIndex((n) => n.id === id);
      if (index !== -1) {
        this.notes[index] = { ...this.notes[index], ...updates };
      }
    },

    deleteLocalNote(id: string) {
      this.notes = this.notes.filter((n) => n.id !== id);
    },

    clearError() {
      this.error = null;
    },

    // Set the current status filter for notes
    setStatusFilter(filter: 'active' | 'completed' | 'cancelled') {
      this.currentFilter = filter;
      // Reset author filter when changing status filter
      this.authorFilter = null;
      // Reset include reminders when changing status filter
      this.includeReminders = false;
      // Reset visibility filter when changing status filter
      this.visibilityFilter = 'all';
    },
  
      // Set the sort order for notes
      setSortOrder(order: 'date-desc' | 'date-asc' | 'title') {
        this.sortOrder = order;
      },

    // Smart merge: Update only specific fields of a note to maintain reactivity
    // Called by Socket Bridge in notification.ts when receiving note:commented events
    // Day.js Protocol: Frontend uses dayjs.utc() for timestamp generation
    patchNoteDates(noteId: string, lastCommentAt: string, lastCommentAuthorId?: string, isMentioned?: boolean, mentionAt?: string) {
      console.log('[DEBUG_PATCH_DATES_INTERNAL]', { noteId, isMentioned, lastCommentAt });
      const index = this.notes.findIndex((n) => n.id === noteId);
      if (index !== -1) {
        console.log('[DEBUG SOCKET PATCH]', { noteId, lastCommentAt, isMentioned, mentionAt });
        const note = this.notes[index];
        // Create a NEW object to ensure Vue reactivity
        this.notes[index] = {
          ...note,
          lastCommentAt,
          lastCommentAuthorId: lastCommentAuthorId || note.lastCommentAuthorId,
          // If this is a mention, update lastMentionAt to trigger hasUserMention getter
          // Use mentionAt if provided, otherwise use lastCommentAt
          lastMentionAt: isMentioned ? (mentionAt || lastCommentAt) : note.lastMentionAt,
          // Day.js Protocol: Используем dayjs.utc() для генерации таймстемпа
          updatedAt: dayjs.utc().toISOString(),
        };
        console.log('[NoteStore] patchNoteDates: Note updated successfully, lastMentionAt:', this.notes[index].lastMentionAt);
      } else {
        console.log('[NoteStore] patchNoteDates: Note not found', noteId);
      }
    },

    // Patch note layout fields (e.g., lastViewedAt) for smart merge
    patchNoteLayout(noteId: string, layoutUpdates: Partial<NoteLayout>) {
      const index = this.notes.findIndex((n) => n.id === noteId);
      if (index !== -1) {
        const note = this.notes[index];
        // Create a NEW object to ensure Vue reactivity
        // Ensure layout exists and has proper defaults
        const currentLayout = note.layout || {
          reminderAt: null,
          lastViewedAt: null,
        };
        this.notes[index] = {
          ...note,
          layout: {
            ...currentLayout,
            ...layoutUpdates,
          } as NoteLayout,
        };
        console.log('[NoteStore] patchNoteLayout: Note layout updated for noteId:', noteId);
      } else {
        console.log('[NoteStore] patchNoteLayout: Note not found', noteId);
      }
    },

    // Единый метод для пометки прочтения (Оптимистичный UI)
    // Atomic Wipe: обновляет lastViewedAt, что гасит все индикаторы одновременно
    // Day.js Protocol: Frontend uses dayjs.utc() for timestamp generation
    async markAsRead(noteId: string) {
      const userStore = useUserStore();
      const notificationStore = useNotificationStore();
      // Day.js Protocol: Используем dayjs.utc() для генерации таймстемпа
      const timestamp = dayjs.utc().toISOString();

      // --- ШАГ 1: МГНОВЕННОЕ ЛОКАЛЬНОЕ ОБНОВЛЕНИЕ (Оптимизм) ---
      console.log('[DEBUG MARK AS READ] Setting lastViewedAt for note:', noteId);
      // Обновляем дату просмотра через patchNoteLayout (создает новый объект)
      this.patchNoteLayout(noteId, { lastViewedAt: timestamp });

      // Гасим уведомления в NotificationStore
      const related = notificationStore.notifications
        .filter(n => n.data?.noteId === noteId && !n.readAt);

      related.forEach(n => {
        // Вызываем локальную пометку (если такой метод есть) или просто меняем объект
        n.readAt = timestamp;
      });

      // --- ШАГ 2: СИНХРОНИЗАЦИЯ С СЕРВЕРОМ (Фон) ---
      try {
        await noteRepository.markNoteAsViewed(noteId);
        // Синхронно помечаем уведомления на бэкенде
        for (const n of related) {
          notificationStore.markAsRead(n.id);
        }
      } catch (e) {
        console.error('Failed to sync read status with server:', e);
        // В случае критической ошибки здесь можно сделать откат (Rollback),
        // но для статуса прочтения это обычно избыточно.
      }
    },

    // Update both viewMode (current state) and preferredViewMode (persistent preference)
    updateViewMode(mode: 'grid' | 'list') {
      this.viewMode = mode;
      this.preferredViewMode = mode;
    },
  },

  persist: {
    // Persist preferredViewMode and sortOrder across page reloads
    // Filters (currentFilter, authorFilter, includeReminders, visibilityFilter) reset on F5
    // viewMode is NOT persisted - it's reset based on currentFilter
    pick: ['preferredViewMode', 'sortOrder'],
  },
});
