'use client'

import React, { useState, useEffect, useCallback, useRef, useTransition, useMemo } from 'react'
import { useSearchParams, useRouter } from 'next/navigation'
import dynamic from 'next/dynamic'
import { Note } from '@/lib/types'
import { getAllNotes, searchNotes, enableNoteHistory, getNoteById, createNote, deleteNote, togglePin, toggleArchive, updateNote, updateFullOrderWithoutRevalidation } from '@/app/actions/notes'
import { NotesListViews, type NotesLayoutMode, type NotesViewType } from '@/components/notes-list-views'
import {
  NOTES_LAYOUT_STORAGE_KEY,
  NOTES_VIEW_TYPE_STORAGE_KEY,
  parseNotesLayoutMode,
  parseNotesViewType,
  setNotesLayoutPreference,
  setNotesViewTypePreference,
} from '@/lib/notes-view-preference'

import { NotebookSuggestionToast } from '@/components/notebook-suggestion-toast'
import { Button } from '@/components/ui/button'
import { Plus, ArrowUpDown, Search, Sparkles, FileText, FolderOpen, ChevronRight, Tag as TagIcon, X, Menu, LayoutGrid, List, Table } from 'lucide-react'
import { emitNoteChange } from '@/lib/note-change-sync'
import { useReminderCheck } from '@/hooks/use-reminder-check'
import { useAutoLabelSuggestion } from '@/hooks/use-auto-label-suggestion'
import { useNotebooks } from '@/context/notebooks-context'
import { cn } from '@/lib/utils'
import { useLanguage } from '@/lib/i18n'
import { useEditorUI } from '@/context/editor-ui-context'
import { NoteHistoryModal } from '@/components/note-history-modal'
import { CreateNotebookDialog } from '@/components/create-notebook-dialog'
import { toast } from 'sonner'
import { AnimatePresence, motion } from 'motion/react'


type SortOrder = 'newest' | 'oldest' | 'alpha' | 'manual'

const NoteEditor = dynamic(
  () => import('@/components/note-editor').then(m => ({ default: m.NoteEditor })),
  { ssr: false }
)
const BatchOrganizationDialog = dynamic(
  () => import('@/components/batch-organization-dialog').then(m => ({ default: m.BatchOrganizationDialog })),
  { ssr: false }
)
const AutoLabelSuggestionDialog = dynamic(
  () => import('@/components/auto-label-suggestion-dialog').then(m => ({ default: m.AutoLabelSuggestionDialog })),
  { ssr: false }
)
const NotebookSummaryDialog = dynamic(
  () => import('@/components/notebook-summary-dialog').then(m => ({ default: m.NotebookSummaryDialog })),
  { ssr: false }
)
const OrganizeNotebookDialog = dynamic(
  () => import('@/components/organize-notebook-dialog').then(m => ({ default: m.OrganizeNotebookDialog })),
  { ssr: false }
)

type InitialSettings = {
  showRecentNotes: boolean
  noteHistory: boolean
  noteHistoryMode: 'manual' | 'auto'
  aiAssistantEnabled: boolean
}

interface HomeClientProps {
  initialNotes: Note[]
  initialSettings: InitialSettings
  initialLayoutMode?: NotesLayoutMode
  initialViewType?: NotesViewType
}

export function HomeClient({
  initialNotes,
  initialSettings,
  initialLayoutMode = 'list',
  initialViewType = 'notes',
}: HomeClientProps) {
  const searchParams = useSearchParams()
  const router = useRouter()
  const { t } = useLanguage()

  const [notes, setNotes] = useState<Note[]>(initialNotes)
  const [pinnedNotes, setPinnedNotes] = useState<Note[]>(
    initialNotes.filter(n => n.isPinned)
  )
  const [noteHistoryMode] = useState<'manual' | 'auto'>(initialSettings.noteHistoryMode)
  const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null)
  const [isLoading, setIsLoading] = useState(false)
  const [notebookSuggestion, setNotebookSuggestion] = useState<{ noteId: string; content: string } | null>(null)
  const [batchOrganizationOpen, setBatchOrganizationOpen] = useState(false)
  const [historyOpen, setHistoryOpen] = useState(false)
  const [historyNote, setHistoryNote] = useState<Note | null>(null)
  const [isCreating, startCreating] = useTransition()
  const [sortOrder, setSortOrder] = useState<SortOrder>('newest')
  const [showSortMenu, setShowSortMenu] = useState(false)
  const [showInlineSearch, setShowInlineSearch] = useState(false)
  const [inlineSearchQuery, setInlineSearchQuery] = useState('')
  const [isSearching, setIsSearching] = useState(false)
  const inlineSearchRef = useRef<HTMLInputElement>(null)
  const searchDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const notesRef = useRef(notes)
  notesRef.current = notes
  const { labels, notebooks, refreshNotebooks } = useNotebooks()
  const labelsRef = useRef(labels)
  labelsRef.current = labels
  const initialNotesRef = useRef(initialNotes)
  initialNotesRef.current = initialNotes
  const { setControls } = useEditorUI()

  const { shouldSuggest: shouldSuggestLabels, notebookId: suggestNotebookId, dismiss: dismissLabelSuggestion } = useAutoLabelSuggestion()
  const [autoLabelOpen, setAutoLabelOpen] = useState(false)
  const [summaryDialogOpen, setSummaryDialogOpen] = useState(false)
  const [createSubNotebookOpen, setCreateSubNotebookOpen] = useState(false)
  const [organizeNotebookOpen, setOrganizeNotebookOpen] = useState(false)
  const [selectedTagIds, setSelectedTagIds] = useState<string[]>([])
  const [isTagsExpanded, setIsTagsExpanded] = useState(false)
  const [tagSearchQuery, setTagSearchQuery] = useState('')
  const [viewType, setViewType] = useState<NotesViewType>(initialViewType)
  const [layoutMode, setLayoutMode] = useState<NotesLayoutMode>(initialLayoutMode)

  useEffect(() => {
    const storedLayout = parseNotesLayoutMode(localStorage.getItem(NOTES_LAYOUT_STORAGE_KEY))
    const storedViewType = parseNotesViewType(localStorage.getItem(NOTES_VIEW_TYPE_STORAGE_KEY))
    if (storedLayout !== initialLayoutMode) {
      setLayoutMode(storedLayout)
      setNotesLayoutPreference(storedLayout)
    }
    if (storedViewType !== initialViewType) {
      setViewType(storedViewType)
      setNotesViewTypePreference(storedViewType)
    }
  }, [initialLayoutMode, initialViewType])

  useEffect(() => {
    setNotesViewTypePreference(viewType)
  }, [viewType])

  useEffect(() => {
    setNotesLayoutPreference(layoutMode)
  }, [layoutMode])

  useEffect(() => {
    const onLayoutChange = (e: Event) => {
      const detail = (e as CustomEvent<{ layout?: NotesLayoutMode }>).detail?.layout
      if (detail === 'grid' || detail === 'list' || detail === 'table') {
        setLayoutMode(detail)
        setViewType('notes')
      }
    }
    window.addEventListener('memento-notes-layout-change', onLayoutChange)
    return () => window.removeEventListener('memento-notes-layout-change', onLayoutChange)
  }, [])

  // Sidebar carnet / inbox: fermer l'éditeur plein écran (comme la ref. architectural-grid)
  useEffect(() => {
    if (searchParams.get('forceList') === '1') {
      setEditingNote(null)
      const params = new URLSearchParams(searchParams.toString())
      params.delete('forceList')
      const newUrl = params.toString() ? `/home?${params.toString()}` : '/home'
      router.replace(newUrl, { scroll: false })
    }
  }, [searchParams, router])

  const notebookFilter = searchParams.get('notebook')

  const fetchNotesForCurrentView = useCallback(
    async (options?: { silent?: boolean }) => {
      const search = searchParams.get('search')?.trim() || null
      const labelFilter = searchParams.get('labels')?.split(',').filter(Boolean) || []
      const colorFilter = searchParams.get('color')
      const notebook = searchParams.get('notebook')
      const sharedOnly = searchParams.get('shared') === '1'
      const remindersOnly = searchParams.get('reminders') === '1'

      if (!options?.silent) {
        setIsLoading(true)
      }

      let allNotes = search
        ? await searchNotes(search, true, notebook || undefined)
        : await getAllNotes(false, notebook || undefined)

      if (sharedOnly) {
        allNotes = allNotes.filter((note: Note) => note._isShared)
      } else if (remindersOnly) {
        allNotes = allNotes.filter((note: Note) => note.reminder !== null)
      } else if (!notebook && !search) {
        allNotes = allNotes.filter((note: Note) => !note.notebookId && !note._isShared)
      }

      if (labelFilter.length > 0) {
        allNotes = allNotes.filter((note: Note) =>
          labelFilter.every((label: string) => note.labels?.includes(label))
        )
      }

      if (colorFilter) {
        const labelNamesWithColor = labelsRef.current
          .filter((label) => label.color === colorFilter)
          .map((label) => label.name)
        allNotes = allNotes.filter((note: Note) =>
          note.labels?.some((label: string) => labelNamesWithColor.includes(label))
        )
      }

      setNotes((prev) => {
        const localSizeMap = new Map(prev.map((n) => [n.id, n.size]))
        return allNotes.map((n) => ({ ...n, size: localSizeMap.get(n.id) ?? n.size }))
      })
      setPinnedNotes(allNotes.filter((n: Note) => n.isPinned))
      setIsLoading(false)
    },
    [searchParams]
  )

  const filterNotesForCurrentView = useCallback((source: Note[]) => {
    const notebook = searchParams.get('notebook')
    const sharedOnly = searchParams.get('shared') === '1'
    const remindersOnly = searchParams.get('reminders') === '1'

    if (notebook) {
      return source.filter((n) => n.notebookId === notebook && !n._isShared)
    }
    if (sharedOnly) {
      return source.filter((n) => n._isShared)
    }
    if (remindersOnly) {
      return source.filter((n) => n.reminder !== null)
    }
    return source.filter((n) => !n.notebookId && !n._isShared)
  }, [searchParams])

  const handleNoteCreated = useCallback((note: Note) => {
    const search = searchParams.get('search')?.trim() || null
    if (search) {
      void fetchNotesForCurrentView({ silent: true })
      emitNoteChange({ type: 'created', note })
      return
    }

    setNotes((prevNotes) => {
      const notebookFilter = searchParams.get('notebook')
      const labelFilter = searchParams.get('labels')?.split(',').filter(Boolean) || []
      const colorFilter = searchParams.get('color')

      if (notebookFilter && note.notebookId !== notebookFilter) return prevNotes
      if (!notebookFilter && note.notebookId) return prevNotes

      if (labelFilter.length > 0) {
        const noteLabels = note.labels || []
        if (!noteLabels.some((label: string) => labelFilter.includes(label))) return prevNotes
      }

      if (colorFilter) {
        const labelNamesWithColor = labels
          .filter((label: any) => label.color === colorFilter)
          .map((label: any) => label.name)
        const noteLabels = note.labels || []
        if (!noteLabels.some((label: string) => labelNamesWithColor.includes(label))) return prevNotes
      }

      const isPinned = note.isPinned || false
      const pinnedNotes = prevNotes.filter(n => n.isPinned)
      const unpinnedNotes = prevNotes.filter(n => !n.isPinned)

      if (isPinned) {
        return [note, ...pinnedNotes, ...unpinnedNotes]
      } else {
        return [...pinnedNotes, note, ...unpinnedNotes]
      }
    })
    emitNoteChange({ type: 'created', note })

    if (!note.notebookId) {
      const wordCount = (note.content || '').trim().split(/\s+/).filter(w => w.length > 0).length
      if (wordCount >= 20) {
        setNotebookSuggestion({ noteId: note.id, content: note.content || '' })
      }
    }
  }, [searchParams, labels, fetchNotesForCurrentView])

  // Always fetch fresh from server — avoids stale state after a save regardless of
  // whether the notes list has re-fetched yet.
  const handleOpenNoteFresh = useCallback(async (noteId: string, readOnly = false) => {
    const note = await getNoteById(noteId)
    if (note) setEditingNote({ note, readOnly })
  }, [])

  const handleAddNote = () => {
    startCreating(async () => {
      try {
        const newNote = await createNote({
          content: '',
          type: 'richtext',
          title: undefined,
          notebookId: notebookFilter || undefined,
          skipRevalidation: true
        })
        if (!newNote) return
        handleNoteCreated(newNote)
        setEditingNote({ note: newNote, readOnly: false })
      } catch {
        toast.error(t('notes.createFailed'))
      }
    })
  }

  const handleOpenHistory = useCallback((note: Note) => {
    setHistoryNote(note)
    setHistoryOpen(true)
  }, [])

  const handleEnableHistory = useCallback(async (noteId: string) => {
    await enableNoteHistory(noteId)
    setNotes((prev) => prev.map((n) => (n.id === noteId ? { ...n, historyEnabled: true } : n)))
    setPinnedNotes((prev) => prev.map((n) => (n.id === noteId ? { ...n, historyEnabled: true } : n)))
    setEditingNote((prev) => (prev?.note.id === noteId ? { ...prev, note: { ...prev.note, historyEnabled: true } } : prev))
    setHistoryNote((prev) => (prev?.id === noteId ? { ...prev, historyEnabled: true } : prev))
  }, [])

  const handleHistoryRestored = useCallback((restored: Note) => {
    setNotes((prev) => prev.map((n) => (n.id === restored.id ? { ...n, ...restored } : n)))
    setPinnedNotes((prev) => prev.map((n) => (n.id === restored.id ? { ...n, ...restored } : n)))
    setEditingNote((prev) => (prev?.note.id === restored.id ? { ...prev, note: restored } : prev))
    setHistoryNote((prev) => (prev?.id === restored.id ? { ...prev, ...restored } : prev))
  }, [])

  const handleSizeChange = useCallback((noteId: string, size: 'small' | 'medium' | 'large') => {
    setNotes(prev => prev.map(n => n.id === noteId ? { ...n, size } : n))
    setPinnedNotes(prev => prev.map(n => n.id === noteId ? { ...n, size } : n))
  }, [])

  const patchNoteInList = useCallback((noteId: string, patch: Partial<Note>) => {
    setNotes((prev) => prev.map((n) => (n.id === noteId ? { ...n, ...patch } : n)))
  }, [])

  const removeNoteFromList = useCallback((noteId: string) => {
    setNotes((prev) => prev.filter((n) => n.id !== noteId))
    setEditingNote((prev) => (prev?.note.id === noteId ? null : prev))
  }, [])

  const noteVisibleInCurrentView = useCallback(
    (note: Pick<Note, 'notebookId' | '_isShared'>) => {
      const notebook = searchParams.get('notebook')
      const sharedOnly = searchParams.get('shared') === '1'
      if (sharedOnly) return !!note._isShared
      if (notebook) return note.notebookId === notebook && !note._isShared
      return !note.notebookId && !note._isShared
    },
    [searchParams]
  )

  const handleTogglePin = useCallback(
    async (note: Note) => {
      const nextPinned = !note.isPinned
      patchNoteInList(note.id, { isPinned: nextPinned })
      emitNoteChange({ type: 'updated', note: { ...note, isPinned: nextPinned } })
      try {
        await togglePin(note.id, nextPinned, { skipRevalidation: true })
      } catch {
        patchNoteInList(note.id, { isPinned: !nextPinned })
        emitNoteChange({ type: 'updated', note: { ...note, isPinned: !nextPinned } })
        toast.error(t('general.error'))
      }
    },
    [patchNoteInList, t]
  )

  const handleDeleteNoteFromList = useCallback(
    async (note: Note) => {
      removeNoteFromList(note.id)
      emitNoteChange({ type: 'deleted', noteId: note.id, notebookId: note.notebookId })
      try {
        await deleteNote(note.id, { skipRevalidation: true })
        toast.success(t('notes.deleted') || 'Note supprimée')
      } catch {
        setNotes((prev) => [note, ...prev])
        emitNoteChange({ type: 'created', note })
        toast.error(t('general.error'))
      }
    },
    [removeNoteFromList, t]
  )

  const handleArchiveNoteFromList = useCallback(
    async (note: Note) => {
      const nextArchived = !note.isArchived
      if (nextArchived) removeNoteFromList(note.id)
      else patchNoteInList(note.id, { isArchived: nextArchived })
      emitNoteChange({ type: 'updated', note: { ...note, isArchived: nextArchived } })
      try {
        await toggleArchive(note.id, nextArchived, { skipRevalidation: true })
        toast.success(
          nextArchived ? (t('notes.archived') || 'Archivée') : (t('notes.unarchived') || 'Désarchivée')
        )
      } catch {
        if (nextArchived) setNotes((prev) => [note, ...prev])
        else patchNoteInList(note.id, { isArchived: !nextArchived })
        toast.error(t('general.error'))
      }
    },
    [patchNoteInList, removeNoteFromList, t]
  )

  const handleMoveNoteToNotebook = useCallback(
    async (note: Note, notebookId: string | null) => {
      const moved = { ...note, notebookId }
      if (noteVisibleInCurrentView(moved)) {
        patchNoteInList(note.id, { notebookId })
      } else {
        removeNoteFromList(note.id)
      }
      emitNoteChange({ type: 'updated', note: moved })
      try {
        await updateNote(note.id, { notebookId }, { skipRevalidation: true })
        toast.success(t('notebookSuggestion.movedToNotebook') || 'Note déplacée')
      } catch {
        if (noteVisibleInCurrentView(note)) {
          patchNoteInList(note.id, { notebookId: note.notebookId ?? null })
        } else {
          setNotes((prev) => [note, ...prev])
        }
        toast.error(t('general.error'))
      }
    },
    [noteVisibleInCurrentView, patchNoteInList, removeNoteFromList, t]
  )

  const handleNoteContentPatch = useCallback((noteId: string, patch: Partial<Note>) => {
    setNotes((prev) => {
      const next = prev.map((n) => (n.id === noteId ? { ...n, ...patch } : n))
      const updated = next.find((n) => n.id === noteId)
      if (updated) emitNoteChange({ type: 'updated', note: updated })
      return next
    })
  }, [])

  const handleNoteIllustrationGenerated = useCallback(async (noteId: string) => {
    const fresh = await getNoteById(noteId)
    if (!fresh) return
    patchNoteInList(noteId, fresh)
    emitNoteChange({ type: 'updated', note: fresh })
  }, [patchNoteInList])

  const handleGridReorder = useCallback(
    async (orderedIds: string[]) => {
      setSortOrder('manual')
      setNotes((prev) => {
        const orderMap = new Map(orderedIds.map((id, index) => [id, index]))
        return prev.map((n) => (orderMap.has(n.id) ? { ...n, order: orderMap.get(n.id)! } : n))
      })
      try {
        await updateFullOrderWithoutRevalidation(orderedIds)
      } catch {
        toast.error(t('general.error'))
      }
    },
    [t],
  )


  // Garder openNote dans l'URL tant que l'éditeur est ouvert → le sidebar peut surligner la note (comme activeNoteId dans la ref.)
  useEffect(() => {
    const openNoteId = searchParams.get('openNote')
    if (!openNoteId) return

    let cancelled = false
    const run = async () => {
      // Always fetch fresh data from DB to avoid showing stale content after a save.
      // notesRef.current can be stale if the notes list hasn't re-fetched yet when the
      // user closes and re-opens the note quickly after saving.
      const note = await getNoteById(openNoteId)
      if (cancelled || !note) return
      setEditingNote({ note, readOnly: false })
    }
    run()
    return () => {
      cancelled = true
    }
  }, [searchParams])

  useEffect(() => {
    const handler = (e: Event) => {
      const { name } = (e as CustomEvent).detail
      const removeLabel = (note: Note) => {
        const currentLabels = note.labels || []
        const updated = currentLabels.filter((l) => l.toLowerCase() !== name.toLowerCase())
        if (updated.length === currentLabels.length) return note
        return { ...note, labels: updated.length > 0 ? updated : null }
      }
      setNotes((prev) => prev.map(removeLabel))
      setPinnedNotes((prev) => prev.map(removeLabel))
    }
    window.addEventListener('label-deleted', handler)
    return () => window.removeEventListener('label-deleted', handler)
  }, [])

  useEffect(() => {
    const search = searchParams.get('search')?.trim() || null
    const labelFilter = searchParams.get('labels')?.split(',').filter(Boolean) || []
    const colorFilter = searchParams.get('color')
    const notebook = searchParams.get('notebook')
    const sharedOnly = searchParams.get('shared') === '1'
    const remindersOnly = searchParams.get('reminders') === '1'
    const hasActiveFilter = !!(search || labelFilter.length > 0 || colorFilter || sharedOnly || remindersOnly)

    if (hasActiveFilter || notebook) {
      void fetchNotesForCurrentView()
      return
    }

    const filtered = filterNotesForCurrentView(initialNotesRef.current)
    setNotes((prev) => {
      const localSizeMap = new Map(prev.map((n) => [n.id, n.size]))
      return filtered.map((n) => ({ ...n, size: localSizeMap.get(n.id) ?? n.size }))
    })
    setPinnedNotes(filtered.filter((n) => n.isPinned))
  }, [searchParams, fetchNotesForCurrentView, filterNotesForCurrentView])

  const currentNotebook = notebooks.find((n: any) => n.id === searchParams.get('notebook'))

  const notebookPath = useMemo(() => {
    if (!currentNotebook) return []
    const trail: any[] = []
    let current: any = currentNotebook
    while (current) {
      trail.unshift(current)
      if (!current.parentId) break
      const parent = notebooks.find((nb: any) => nb.id === current.parentId)
      if (!parent) break
      current = parent
    }
    return trail
  }, [currentNotebook, notebooks])

  const availableTags = useMemo(() => {
    const tagsMap = new Map<string, { id: string; name: string; type?: string }>()
    const carnetNotes = notes
    carnetNotes.forEach(note => {
      ;(note.labels || []).forEach(labelName => {
        if (!tagsMap.has(labelName)) {
          const labelObj = labels.find((l: any) => l.name === labelName)
          tagsMap.set(labelName, {
            id: labelObj?.id || labelName,
            name: labelName,
            type: labelObj?.type,
          })
        }
      })
    })
    return Array.from(tagsMap.values()).sort((a, b) => {
      if (a.type === 'ai' && b.type !== 'ai') return -1
      if (a.type !== 'ai' && b.type === 'ai') return 1
      return a.name.localeCompare(b.name)
    })
  }, [notes, labels])

  const visibleTags = useMemo(() => {
    let filtered = availableTags
    if (tagSearchQuery) {
      filtered = availableTags.filter(t =>
        t.name.toLowerCase().includes(tagSearchQuery.toLowerCase())
      )
    } else if (!isTagsExpanded) {
      filtered = availableTags.slice(0, 10)
      selectedTagIds.forEach(id => {
        if (!filtered.find(t => t.id === id)) {
          const tag = availableTags.find(t => t.id === id)
          if (tag) filtered.push(tag)
        }
      })
    }
    return filtered
  }, [availableTags, isTagsExpanded, tagSearchQuery, selectedTagIds])

  const toggleTag = useCallback((tagId: string) => {
    setSelectedTagIds(prev =>
      prev.includes(tagId) ? prev.filter(id => id !== tagId) : [...prev, tagId]
    )
  }, [])


  useEffect(() => {
    setControls({
      openNoteComposer: () => handleAddNote(),
    })
    return () => setControls(null)
  }, [setControls])

  // Apply sort order to notes
  const sortedNotes = useMemo(() => {
    let sorted = [...notes]
    if (sortOrder === 'newest') sorted.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
    if (sortOrder === 'oldest') sorted.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
    if (sortOrder === 'alpha') sorted.sort((a, b) => (a.title || '').localeCompare(b.title || ''))
    if (sortOrder === 'manual') sorted.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))

    if (selectedTagIds.length > 0) {
      const selectedNames = selectedTagIds
        .map(id => availableTags.find(t => t.id === id)?.name)
        .filter(Boolean) as string[]
      sorted = sorted.filter(note =>
        selectedNames.every(name => (note.labels || []).includes(name))
      )
    }

    return sorted
  }, [notes, sortOrder, selectedTagIds, availableTags])

  const sortedPinnedNotes = useMemo(() => {
    return sortedNotes.filter(n => n.isPinned)
  }, [sortedNotes])

  const sortLabels: Record<SortOrder, string> = {
    newest: t('sidebar.sortNewest'),
    oldest: t('sidebar.sortOldest'),
    alpha: t('sidebar.sortAlpha'),
    manual: t('sidebar.sortManual'),
  }


  const handleEditorClose = useCallback(() => {
    setEditingNote(null)
    const params = new URLSearchParams(searchParams.toString())
    params.delete('openNote')
    const qs = params.toString()
    router.replace(qs ? `/home?${qs}` : '/home', { scroll: false })
  }, [router, searchParams])

  const handleNoteSaved = useCallback((savedNote: Note) => {
    setNotes((prev) => prev.map((n) => (n.id === savedNote.id ? { ...n, ...savedNote } : n)))
    setEditingNote((prev) => (prev?.note.id === savedNote.id ? { ...prev, note: savedNote } : prev))
    emitNoteChange({ type: 'updated', note: savedNote })
  }, [])

  return (
    <div
      className={cn(
        'flex w-full min-h-0 flex-1 flex-col gap-3 py-1'
      )}
    >
      {editingNote ? (
        <NoteEditor
          note={editingNote.note}
          readOnly={editingNote.readOnly}
          onClose={handleEditorClose}
          onNoteSaved={handleNoteSaved}
          fullPage
        />
      ) : (
        <div className="flex-1 overflow-y-auto min-h-0 bg-memento-paper dark:bg-background flex flex-col">
          <div
            className="px-4 sm:px-8 md:px-12 pt-6 sm:pt-10 md:pt-12 pb-8 flex flex-col gap-6 sticky top-0 bg-memento-paper/90 dark:bg-background/90 backdrop-blur-md z-30"
          >
            <div className="flex justify-between items-start gap-3">
              {/* Hamburger mobile — ouvre la sidebar */}
              <button
                className="md:hidden p-2 -ms-2 text-foreground hover:bg-foreground/5 rounded-lg transition-colors shrink-0 self-start mt-1"
                onClick={() => window.dispatchEvent(new CustomEvent('open-mobile-sidebar'))}
                aria-label="Open menu"
              >
                <Menu size={22} />
              </button>
              <div className="flex-1 min-w-0">
                {currentNotebook && notebookPath.length > 0 && (
                  <div
                    className="flex items-center gap-2 text-[12px] uppercase tracking-[.2em] font-bold mb-2 text-ink/60"
                  >
                    {notebookPath.map((nb: any, i: number) => (
                      <React.Fragment key={nb.id}>
                        {i > 0 && <ChevronRight size={10} className="shrink-0 text-concrete" />}
                        <span className={i === notebookPath.length - 1 ? 'text-ink' : 'text-concrete'}>
                          {nb.name}
                        </span>
                      </React.Fragment>
                    ))}
                  </div>
                )}
                <h1 className="font-memento-serif text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight text-foreground leading-tight pe-4 sm:pe-12">
                  {currentNotebook 
                    ? currentNotebook.name 
                    : searchParams.get('shared') === '1' 
                      ? t('sidebar.sharedWithMe')
                      : searchParams.get('reminders') === '1'
                        ? t('sidebar.reminders')
                        : t('notes.title')}
                </h1>
              </div>
            </div>

            <div className="flex items-center justify-between border-b border-foreground/5 pb-4">
              <div className="flex items-center gap-6">
                <button
                  onClick={handleAddNote}
                  disabled={isCreating}
                  className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
                >
                  <Plus size={16} />
                  <span>{t('notes.newNote')}</span>
                </button>

                {currentNotebook && (
                  <button
                    onClick={() => setCreateSubNotebookOpen(true)}
                    className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
                  >
                    <FolderOpen size={16} />
                    <span>{t('notebook.createSubNotebook')}</span>
                  </button>
                )}

                {/* Inline search — toggles an input within the toolbar */}
                {showInlineSearch ? (
                  <div className="flex items-center gap-2">
                    {isSearching ? (
                      <div className="w-3.5 h-3.5 border border-muted-foreground/50 border-t-foreground rounded-full animate-spin shrink-0" />
                    ) : (
                      <Search size={14} className="text-muted-foreground shrink-0" />
                    )}
                    <input
                      ref={inlineSearchRef}
                      autoFocus
                      type="text"
                      value={inlineSearchQuery}
                      onChange={e => {
                        const q = e.target.value
                        setInlineSearchQuery(q)
                        setIsSearching(true)
                        if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current)
                        searchDebounceRef.current = setTimeout(() => {
                          const params = new URLSearchParams(searchParams.toString())
                          if (q.trim()) {
                            params.set('search', q)
                          } else {
                            params.delete('search')
                          }
                          router.push(`/home?${params.toString()}`)
                          setIsSearching(false)
                        }, 300)
                      }}
                      onBlur={() => {
                        if (!inlineSearchQuery) {
                          setShowInlineSearch(false)
                        }
                      }}
                      onKeyDown={e => {
                        if (e.key === 'Escape') {
                          if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current)
                          setShowInlineSearch(false)
                          setInlineSearchQuery('')
                          setIsSearching(false)
                          const params = new URLSearchParams(searchParams.toString())
                          params.delete('search')
                          router.push(`/home?${params.toString()}`)
                        }
                      }}
                      placeholder={t('search.placeholder')}
                      className="w-36 sm:w-48 bg-transparent border-b border-foreground/20 focus:border-foreground outline-none text-[13px] text-foreground placeholder:text-muted-foreground/50 py-0.5 transition-colors"
                    />
                    {inlineSearchQuery && (
                      <button
                        onClick={() => {
                          if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current)
                          setShowInlineSearch(false)
                          setInlineSearchQuery('')
                          setIsSearching(false)
                          const params = new URLSearchParams(searchParams.toString())
                          params.delete('search')
                          router.push(`/home?${params.toString()}`)
                        }}
                        className="text-muted-foreground hover:text-foreground transition-colors"
                      >
                        <X size={12} />
                      </button>
                    )}
                  </div>
                ) : (
                  <button
                    onClick={() => {
                      setShowInlineSearch(true)
                      setTimeout(() => inlineSearchRef.current?.focus(), 50)
                    }}
                    className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
                  >
                    <Search size={16} />
                    <span>{t('notes.search')}</span>
                  </button>
                )}

                {!searchParams.get('notebook') && searchParams.get('shared') !== '1' && (
                  <button
                    onClick={() => setBatchOrganizationOpen(true)}
                    className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
                  >
                    <Sparkles size={16} />
                    <span>{t('notes.reorganize')}</span>
                  </button>
                )}

                {currentNotebook && initialSettings.aiAssistantEnabled && (
                  <button
                    onClick={() => setOrganizeNotebookOpen(true)}
                    className="flex items-center gap-2 text-[13px] text-brand-accent font-medium hover:opacity-70 transition-opacity"
                    title={t('notebook.organizeNotebookWithAITooltip')}
                  >
                    <Sparkles size={16} />
                    <span>{t('batch.organize')}</span>
                  </button>
                )}
              </div>
              <div className="flex items-center gap-4 flex-wrap">
                <div className="bg-foreground/[0.03] dark:bg-white/[0.04] p-0.5 rounded-full flex border border-border/30">
                  <button
                    type="button"
                    onClick={() => setViewType('notes')}
                    className={cn(
                      'px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider transition-all',
                      viewType === 'notes'
                        ? 'bg-foreground text-background shadow-sm'
                        : 'text-muted-foreground hover:text-foreground',
                    )}
                  >
                    {t('notes.viewNotes')}
                  </button>
                  <button
                    type="button"
                    onClick={() => setViewType('tasks')}
                    className={cn(
                      'px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider transition-all',
                      viewType === 'tasks'
                        ? 'bg-foreground text-background shadow-sm'
                        : 'text-muted-foreground hover:text-foreground',
                    )}
                  >
                    {t('notes.viewTasks')}
                  </button>
                </div>

                {viewType === 'notes' && (
                  <div className="bg-foreground/[0.03] dark:bg-white/[0.04] p-0.5 rounded-full flex border border-border/30 items-center">
                    <button
                      type="button"
                      onClick={() => setLayoutMode('grid')}
                      className={cn(
                        'p-1.5 rounded-full transition-all',
                        layoutMode === 'grid'
                          ? 'bg-foreground text-background shadow-sm'
                          : 'text-muted-foreground hover:text-foreground',
                      )}
                      title={t('notes.layoutGridTitle')}
                    >
                      <LayoutGrid size={13} />
                    </button>
                    <button
                      type="button"
                      onClick={() => setLayoutMode('list')}
                      className={cn(
                        'p-1.5 rounded-full transition-all',
                        layoutMode === 'list'
                          ? 'bg-foreground text-background shadow-sm'
                          : 'text-muted-foreground hover:text-foreground',
                      )}
                      title={t('notes.layoutListTitle')}
                    >
                      <List size={13} />
                    </button>
                    <button
                      type="button"
                      onClick={() => setLayoutMode('table')}
                      className={cn(
                        'p-1.5 rounded-full transition-all',
                        layoutMode === 'table'
                          ? 'bg-foreground text-background shadow-sm'
                          : 'text-muted-foreground hover:text-foreground',
                      )}
                      title={t('notes.layoutTableTitle')}
                    >
                      <Table size={13} />
                    </button>
                  </div>
                )}

                {searchParams.get('notebook') && (
                  <button
                    onClick={() => setSummaryDialogOpen(true)}
                    disabled={!initialSettings.aiAssistantEnabled}
                    className={cn(
                      "flex items-center gap-2 text-[13px] font-medium transition-opacity",
                      initialSettings.aiAssistantEnabled ? "text-foreground hover:opacity-70" : "text-muted-foreground opacity-50 cursor-not-allowed"
                    )}
                    title={initialSettings.aiAssistantEnabled ? t('notebook.summary') : t('notebook.assistantRequiredForSummarize')}
                  >
                    <FileText size={16} />
                    <span>{t('notebook.summary')}</span>
                  </button>
                )}
                <button
                  onClick={() => setSortOrder(s => s === 'newest' ? 'oldest' : s === 'oldest' ? 'alpha' : s === 'alpha' ? 'manual' : 'newest')}
                  className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
                >
                  <ArrowUpDown size={16} />
                  <span>{sortLabels[sortOrder]}</span>
                </button>
              </div>
            </div>

            {availableTags.length > 0 && (
              <div className="flex flex-col gap-3">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-3 text-[10px] font-bold uppercase tracking-[0.2em] text-muted-foreground">
                    <TagIcon size={12} />
                    <span>{t('labels.filterByTags')}</span>
                    {selectedTagIds.length > 0 && (
                      <span className="bg-brand-accent/10 text-brand-accent px-2 py-0.5 rounded-full text-[9px] lowercase tracking-normal">
                        {selectedTagIds.length} active
                      </span>
                    )}
                  </div>
                  {availableTags.length > 10 && (
                    <input
                      type="text"
                      placeholder={t('labels.searchTags')}
                      className="bg-transparent border-b border-foreground/10 text-[10px] outline-none focus:border-brand-accent/40 py-1 px-2 w-32 transition-all focus:w-48 placeholder:text-muted-foreground/40"
                      value={tagSearchQuery}
                      onChange={e => setTagSearchQuery(e.target.value)}
                    />
                  )}
                </div>

                <div className="flex flex-wrap gap-2 items-center min-h-[32px]">
                  <AnimatePresence mode="popLayout">
                    {visibleTags.map(tag => {
                      const isActive = selectedTagIds.includes(tag.id)
                      return (
                        <motion.button
                          layout
                          initial={{ opacity: 0, scale: 0.9 }}
                          animate={{ opacity: 1, scale: 1 }}
                          exit={{ opacity: 0, scale: 0.9 }}
                          key={tag.id}
                          onClick={() => toggleTag(tag.id)}
                          className={cn(
                            'px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-wider transition-all border flex items-center gap-2',
                            isActive
                              ? 'bg-foreground text-background border-foreground shadow-sm'
                              : 'bg-card/40 border-border text-muted-foreground hover:border-foreground/30 hover:bg-card/60',
                          )}
                        >
                          {tag.type === 'ai' && (
                            <Sparkles
                              size={10}
                              className={isActive ? 'text-brand-accent' : 'text-brand-accent/60'}
                            />
                          )}
                          {tag.name}
                          {isActive && <X size={10} />}
                        </motion.button>
                      )
                    })}
                  </AnimatePresence>

                  {availableTags.length > 10 && !tagSearchQuery && (
                    <button
                      onClick={() => setIsTagsExpanded(!isTagsExpanded)}
                      className="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60 hover:text-foreground transition-colors border border-dashed border-border rounded-full"
                    >
                      {isTagsExpanded
                        ? t('labels.showLess')
                        : `+ ${availableTags.length - 10} more`}
                    </button>
                  )}

                  {selectedTagIds.length > 0 && (
                    <button
                      onClick={() => setSelectedTagIds([])}
                      className="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-red-500 hover:underline ms-auto"
                    >
                      {t('labels.clearAll')}
                    </button>
                  )}
                </div>
              </div>
            )}
          </div>

          <div className="px-4 sm:px-8 md:px-12 flex-1 pb-10 sm:pb-16 md:pb-20">
            {isLoading ? (
              <div className="text-center py-8 text-muted-foreground">{t('general.loading')}</div>
            ) : notes.length === 0 ? (
              <div className="h-64 flex flex-col items-center justify-center text-center space-y-4">
                <p className="font-memento-serif text-xl italic text-muted-foreground">
                  {t('notes.emptyState')}
                </p>
                <button
                  onClick={handleAddNote}
                  disabled={isCreating}
                  className="px-6 py-2 border border-foreground text-[13px] uppercase tracking-[0.2em] hover:bg-foreground hover:text-background transition-all"
                >
                  {t('notes.createFirst')}
                </button>
              </div>
            ) : (
              <NotesListViews
                notes={sortedNotes}
                pinnedNotes={sortedPinnedNotes}
                viewType={viewType}
                layoutMode={layoutMode}
                onOpen={(note, readOnly) => handleOpenNoteFresh(note.id, readOnly ?? false)}
                onOpenHistory={handleOpenHistory}
                notebookName={currentNotebook?.name}
                onTogglePin={handleTogglePin}
                onDeleteNote={handleDeleteNoteFromList}
                onArchiveNote={handleArchiveNoteFromList}
                onMoveToNotebook={handleMoveNoteToNotebook}
                onNotePatch={handleNoteContentPatch}
                onNoteIllustrationGenerated={handleNoteIllustrationGenerated}
                onGridReorder={handleGridReorder}
              />
            )}
          </div>

          <footer className="px-4 sm:px-8 md:px-12 py-4 sm:py-6 border-t border-foreground/5 text-center mt-auto">
            <p className="text-[11px] text-muted-foreground uppercase tracking-[0.2em] font-medium">
              Memento &mdash; {new Date().getFullYear()}
            </p>
          </footer>
        </div>
      )}

      {notebookSuggestion && (
        <NotebookSuggestionToast
          noteId={notebookSuggestion.noteId}
          noteContent={notebookSuggestion.content}
          onDismiss={() => setNotebookSuggestion(null)}
        />
      )}

      {batchOrganizationOpen && (
        <BatchOrganizationDialog
          open={batchOrganizationOpen}
          onOpenChange={setBatchOrganizationOpen}
          onNotesMoved={() => fetchNotesForCurrentView({ silent: true })}
        />
      )}

      {autoLabelOpen && (
        <AutoLabelSuggestionDialog
          open={autoLabelOpen}
          onOpenChange={(open) => {
            setAutoLabelOpen(open)
            if (!open) dismissLabelSuggestion()
          }}
          notebookId={suggestNotebookId}
          onLabelsCreated={() => fetchNotesForCurrentView({ silent: true })}
        />
      )}

      <NoteHistoryModal
        open={historyOpen}
        onOpenChange={setHistoryOpen}
        note={historyNote}
        enabled={!!historyNote?.historyEnabled}
        onEnableHistory={async () => { if (historyNote) await handleEnableHistory(historyNote.id) }}
        onRestored={handleHistoryRestored}
      />

      {searchParams.get('notebook') && (
        <NotebookSummaryDialog
          open={summaryDialogOpen}
          onOpenChange={setSummaryDialogOpen}
          notebookId={searchParams.get('notebook')}
          notebookName={currentNotebook?.name}
        />
      )}
      {searchParams.get('notebook') && (
        <CreateNotebookDialog
          open={createSubNotebookOpen}
          onOpenChange={setCreateSubNotebookOpen}
          parentNotebookId={searchParams.get('notebook')}
        />
      )}
      {currentNotebook && (
        <OrganizeNotebookDialog
          open={organizeNotebookOpen}
          onOpenChange={setOrganizeNotebookOpen}
          notebookId={currentNotebook.id}
          notebookName={currentNotebook.name}
          onDone={() => {
            refreshNotebooks()
          }}
        />
      )}
    </div>
  )
}
