'use client'

import Link from 'next/link'
import { usePathname, useSearchParams, useRouter } from 'next/navigation'
import { cn } from '@/lib/utils'
import {
  Settings,
  Plus,
  ChevronRight,
  Lock,
  BookOpen,
  BookMarked,
  Bot,
  Inbox,
  CalendarDays,
  FlaskConical,
  ArrowUpDown,
  Archive,
  Trash2,
  User,
  LogOut,
  Shield,
  GripVertical,
  Users,
  Bell,
  Pencil,
  Clock,
  Moon,
  Sun,
  Pin,
  PinOff,
  Sparkles,
  Home,
  Search,
  GraduationCap,
  FileText,
  Folder,
  FolderOpen,
} from 'lucide-react'
import { useSearchModal } from '@/context/search-modal-context'
import { useLanguage } from '@/lib/i18n'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { applyDocumentTheme } from '@/lib/apply-document-theme'
import { getAllNotes, getTrashCount, getNotesWithReminders, toggleReminderDone } from '@/app/actions/notes'
import { NOTE_CHANGE_EVENT, type NoteChangeEvent } from '@/lib/note-change-sync'
import { useNotebooks } from '@/context/notebooks-context'
import { Notebook, Note } from '@/lib/types'
import { toast } from 'sonner'
import { motion, AnimatePresence } from 'motion/react'
import { getNoteDisplayTitle } from '@/lib/note-preview'
import { CreateNotebookDialog } from './create-notebook-dialog'
import { AiNotebookWizard } from './wizard/ai-notebook-wizard'
import { NotificationPanel } from './notification-panel'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { performSignOut } from '@/lib/auth-client'
import { useBrainstormSessions, useDeleteBrainstorm } from '@/hooks/use-brainstorm'
import { UsageMeter } from './usage-meter'
import { StarterPackBadge } from './onboarding/starter-pack-badge'

type NavigationView = 'notebooks' | 'agents' | 'reminders' | 'brainstorms' | 'revision' | 'insights'
type SortOrder = 'newest' | 'oldest' | 'alpha' | 'manual'

function NoteLink({
  title,
  isActive,
  isPinned,
  onClick,
}: {
  title: string
  isActive: boolean
  isPinned?: boolean
  onClick: () => void
}) {
  const { language } = useLanguage()
  const slideX = language === 'fa' || language === 'ar' ? 10 : -10
  return (
    <motion.button
      initial={{ opacity: 0, x: slideX }}
      animate={{ opacity: 1, x: 0 }}
      onClick={onClick}
      className={cn(
        'w-full flex items-center gap-2 ps-6 pe-3 py-1.5 text-[11px] transition-all rounded-lg text-start',
        isActive
          ? 'bg-white dark:bg-white/10 shadow-sm border border-border/50 text-foreground font-semibold'
          : 'text-muted-foreground hover:text-foreground hover:bg-white/30 dark:hover:bg-white/5',
      )}
    >
      <FileText
        size={12}
        className={cn('shrink-0', isActive ? 'text-brand-accent' : 'text-muted-foreground/70')}
      />
      <span className="truncate flex-1">{title}</span>
      {isPinned && <Pin size={10} className="text-amber-500 fill-amber-500 shrink-0" />}
    </motion.button>
  )
}

function SidebarBrainstorms() {
  const { data: sessions, isLoading } = useBrainstormSessions()
  const deleteBrainstorm = useDeleteBrainstorm()
  const router = useRouter()
  const { t } = useLanguage()

  if (isLoading) {
    return (
      <div className="px-4 space-y-2">
        {[1, 2, 3].map(i => (
          <div key={i} className="h-12 rounded-xl bg-paper/50 animate-pulse" />
        ))}
      </div>
    )
  }

  if (!sessions || sessions.length === 0) {
    return (
      <div className="px-4 py-6 text-center">
        <Sparkles size={20} className="mx-auto text-brand-accent/40 mb-2" />
        <p className="text-[11px] text-concrete">{t('brainstorm.noSessions')}</p>
        <button
          onClick={() => router.push('/brainstorm')}
          className="mt-2 text-[11px] text-brand-accent hover:text-brand-accent/80 font-medium"
        >
          {t('brainstorm.startOne')} →
        </button>
      </div>
    )
  }

  return (
    <div className="space-y-0.5">
      {sessions.slice(0, 10).map(s => (
        <div key={s.id} className="relative group/item">
          <button
            onClick={() => router.replace(`/brainstorm?session=${s.id}`)}
            className={`w-full flex items-center gap-3 px-4 py-2.5 rounded-xl transition-all duration-200 text-start hover:bg-brand-accent/5 group ${
              (s as any)._owned === false ? 'border-s-2 border-brand-accent/30 dark:border-brand-accent/70' : ''
            }`}
          >
            <div className={`w-7 h-7 rounded-full flex items-center justify-center shrink-0 ${
              (s as any)._owned === false
                ? 'border border-brand-accent/20 dark:border-brand-accent/80 bg-brand-accent/5 dark:bg-brand-accent/20'
                : 'border border-brand-accent/20 dark:border-brand-accent/40 bg-brand-accent/5 dark:bg-brand-accent/20'
            }`}>
              <Sparkles size={12} className="text-brand-accent" />
            </div>
            <div className="min-w-0 flex-1">
              <p className="text-[12px] font-medium truncate">
                {s.seedIdea}
                {(s as any)._owned === false && (
                  <span className="text-[9px] ms-1.5 text-brand-accent/70 font-normal">{t('sidebar.sharedNotebookBadge')}</span>
                )}
              </p>
              <p className="text-[10px] text-muted-foreground">
                {s.activeIdeas} {t('brainstorm.ideas')} · {new Date(s.createdAt).toLocaleDateString()}
              </p>
            </div>
          </button>
          {(s as any)._owned !== false && (
            <button
              onClick={(e) => {
                e.stopPropagation()
                deleteBrainstorm.mutate(s.id)
              }}
              className="absolute end-2 top-1/2 -translate-y-1/2 p-1.5 rounded-lg opacity-0 group-hover/item:opacity-100 hover:bg-rose-500/10 text-muted-foreground hover:text-rose-500 transition-all"
            >
              <Trash2 size={13} />
            </button>
          )}
        </div>
      ))}
    </div>
  )
}

function SidebarReminders({ onOpenNote }: { onOpenNote: (noteId: string, notebookId: string | null) => void }) {
  const { t } = useLanguage()
  const [reminders, setReminders] = useState<
    { id: string; title: string | null; reminder: Date | string | null; isReminderDone: boolean; notebookId: string | null }[]
  >([])
  const [loading, setLoading] = useState(true)
  const [togglingId, setTogglingId] = useState<string | null>(null)

  const reload = () => {
    getNotesWithReminders().then((rows) => setReminders(rows as typeof reminders))
  }

  useEffect(() => {
    let cancelled = false
    setLoading(true)
    getNotesWithReminders()
      .then((rows) => {
        if (!cancelled) setReminders(rows as typeof reminders)
      })
      .finally(() => {
        if (!cancelled) setLoading(false)
      })
    return () => { cancelled = true }
  }, [])

  const handleComplete = async (e: React.MouseEvent, noteId: string) => {
    e.stopPropagation()
    setTogglingId(noteId)
    await toggleReminderDone(noteId, true)
    setReminders(prev => prev.map(r => r.id === noteId ? { ...r, isReminderDone: true } : r))
    setTogglingId(null)
  }

  const handleSnooze = async (e: React.MouseEvent, noteId: string) => {
    e.stopPropagation()
    // Snooze = +1h
    const snoozeDate = new Date(Date.now() + 60 * 60 * 1000)
    setTogglingId(noteId)
    try {
      await fetch(`/api/notes/${noteId}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ reminder: snoozeDate.toISOString(), isReminderDone: false }),
      })
      setReminders(prev => prev.map(r => r.id === noteId ? { ...r, reminder: snoozeDate, isReminderDone: false } : r))
    } catch { reload() }
    setTogglingId(null)
  }

  if (loading) {
    return (
      <div className="px-4 space-y-2">
        {[1, 2, 3].map((i) => (
          <div key={i} className="h-10 rounded-xl bg-paper/50 animate-pulse" />
        ))}
      </div>
    )
  }

  const now = new Date()
  const active = reminders.filter((r) => !r.isReminderDone && r.reminder)
  const overdue = active.filter((r) => new Date(r.reminder!) < now)
  const upcoming = active.filter((r) => new Date(r.reminder!) >= now)

  if (active.length === 0) {
    return (
      <div className="px-4 py-8 text-center border border-dashed border-border rounded-2xl bg-paper/30 mx-4">
        <Clock size={24} className="mx-auto text-concrete/40 mb-3" />
        <p className="text-[11px] text-concrete italic">{t('reminders.emptyDescription')}</p>
      </div>
    )
  }

  const renderItem = (note: (typeof active)[0], overdueItem?: boolean) => (
    <div
      key={note.id}
      className="group flex items-center gap-1 px-2 py-1.5 rounded-xl hover:bg-brand-accent/5 transition-colors"
    >
      {/* Bouton compléter */}
      <button
        type="button"
        onClick={(e) => { void handleComplete(e, note.id) }}
        disabled={togglingId === note.id}
        className="flex-shrink-0 w-5 h-5 rounded-full border-2 border-concrete/30 hover:border-emerald-500 hover:bg-emerald-50 transition-all flex items-center justify-center"
        title={t('reminders.markDone') || 'Marquer comme fait'}
      >
        {togglingId === note.id
          ? <div className="w-2 h-2 rounded-full bg-concrete animate-pulse" />
          : null}
      </button>

      {/* Contenu cliquable */}
      <button
        type="button"
        onClick={() => onOpenNote(note.id, note.notebookId)}
        className="flex-1 text-start min-w-0"
      >
        <p className="text-[12px] font-medium truncate group-hover:text-brand-accent transition-colors">
          {note.title || t('notes.untitled')}
        </p>
        <p className={cn('text-[10px] mt-0.5', overdueItem ? 'text-red-500' : 'text-muted-foreground')}>
          {note.reminder &&
            new Date(note.reminder).toLocaleString(undefined, {
              month: 'short',
              day: 'numeric',
              hour: '2-digit',
              minute: '2-digit',
            })}
        </p>
      </button>

      {/* Actions (visibles au hover) */}
      <div className="flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity flex gap-0.5">
        <button
          type="button"
          onClick={(e) => { void handleSnooze(e, note.id) }}
          disabled={togglingId === note.id}
          className="p-1 rounded-md hover:bg-amber-100 text-amber-600 transition-colors"
          title={t('reminders.snooze1h') || 'Reporter de 1h'}
        >
          <span className="text-[9px] font-bold">+1h</span>
        </button>
      </div>
    </div>
  )

  return (
    <div className="space-y-4 pb-2">
      {overdue.length > 0 && (
        <div>
          <p className="text-[9px] font-bold uppercase tracking-widest text-red-500 px-4 mb-1">
            {t('reminders.overdue')}
          </p>
          <div className="space-y-0.5">{overdue.map((n) => renderItem(n, true))}</div>
        </div>
      )}
      {upcoming.length > 0 && (
        <div>
          <p className="text-[9px] font-bold uppercase tracking-widest text-muted-foreground px-4 mb-1">
            {t('reminders.upcoming')}
          </p>
          <div className="space-y-0.5">{upcoming.map((n) => renderItem(n))}</div>
        </div>
      )}
    </div>
  )
}

function SidebarCarnetItem({
  carnet,
  isActive,
  notes,
  activeNoteId,
  onCarnetClick,
  onNoteClick,
  onAddSubNotebook,
  onRename,
  onDelete,
  onTogglePin,
  isPinned,
  children,
  isDragging,
  dragHandleProps,
  level,
  isExpanded,
  toggleExpand,
  hasChildNotebooks,
  hasActiveDescendant,
}: {
  carnet: { id: string; name: string; initial: string; isPrivate?: boolean }
  isActive: boolean
  notes: { id: string; title: string; isPinned?: boolean }[]
  activeNoteId: string | null
  onCarnetClick: () => void
  onNoteClick: (noteId: string, carnetId: string) => void
  onAddSubNotebook: () => void
  onRename: () => void
  onDelete: () => void
  onTogglePin: () => void
  isPinned: boolean
  children?: React.ReactNode
  isDragging?: boolean
  dragHandleProps?: React.HTMLAttributes<HTMLDivElement>
  level: number
  isExpanded: boolean
  toggleExpand: () => void
  hasChildNotebooks?: boolean
  hasActiveDescendant?: boolean
}) {
  const { t, language } = useLanguage()
  const isRtl = language === 'fa' || language === 'ar'
  const hasChildren = hasChildNotebooks || React.Children.count(children) > 0 || notes.length > 0
  const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null)

  // Close context menu on outside click
  useEffect(() => {
    if (!contextMenu) return
    const handler = () => setContextMenu(null)
    window.addEventListener('click', handler)
    return () => window.removeEventListener('click', handler)
  }, [contextMenu])

  return (
    <div className={cn('transition-opacity', isDragging && 'opacity-40')}>
      <div
        className="flex items-center group relative h-10"
        style={{ paddingInlineStart: `${level * 24 + 8}px` }}
        onContextMenu={(e) => {
          e.preventDefault()
          e.stopPropagation()
          setContextMenu({ x: e.clientX, y: e.clientY })
        }}
      >
        {level > 0 && (
          <div className="absolute start-[8px] top-[-10px] bottom-1/2 w-px bg-border/40" />
        )}
        {level > 0 && (
          <div className="absolute start-[8px] top-1/2 w-[8px] h-px bg-border/40" />
        )}

        <div className="flex-1 flex items-center gap-0.5">
          {/* Drag handle — visible au hover, dans son propre slot avant le chevron */}
          <div
            {...dragHandleProps}
            className="shrink-0 w-5 h-5 flex items-center justify-center rounded text-muted-foreground/0 group-hover:text-muted-foreground/50 hover:!text-muted-foreground cursor-grab active:cursor-grabbing transition-colors"
            onClick={(e) => e.stopPropagation()}
          >
            <GripVertical size={11} />
          </div>

          {hasChildren ? (
            <button
              onClick={(e) => { e.stopPropagation(); toggleExpand() }}
              className="shrink-0 w-5 h-5 flex items-center justify-center hover:bg-foreground/5 rounded-md transition-colors text-muted-foreground"
            >
              <motion.div animate={{ rotate: isExpanded ? 90 : 0 }} transition={{ duration: 0.2 }}>
                <ChevronRight size={13} className="rtl:scale-x-[-1]" />
              </motion.div>
            </button>
          ) : (
            <div className="w-5" />
          )}

          <motion.div
            whileHover={{ x: isRtl ? -2 : 2 }}
            onClick={onCarnetClick}
            onDoubleClick={(e) => { e.stopPropagation(); onRename() }}
            className={cn(
              'flex-1 flex items-center gap-2.5 px-2 py-1.5 rounded-lg transition-all duration-300 group/item cursor-pointer relative overflow-hidden',
              isActive ? 'bg-white dark:bg-white/10 shadow-sm border border-border/40' : 'hover:bg-white/40 dark:hover:bg-white/5'
            )}
          >
            {isActive && (
              <motion.div
                layoutId="active-indicator"
                className="absolute -start-1 w-1 h-4 bg-brand-accent rounded-full"
                transition={{ type: 'spring', stiffness: 300, damping: 30 }}
              />
            )}
            <div className={cn(
              'w-5 h-5 flex items-center justify-center shrink-0 transition-colors',
              isActive ? 'text-brand-accent' : 'text-muted-foreground/80',
            )}>
              {isExpanded ? <FolderOpen size={13} /> : <Folder size={13} />}
            </div>

            <div className="flex-1 text-start flex items-center gap-2 min-w-0">
              <span className={cn(
                'text-[12px] font-medium transition-colors truncate',
                isActive ? 'text-ink' : 'text-muted-ink group-hover/item:text-ink'
              )}>
                {carnet.name}
              </span>
              {carnet.isPrivate && <Lock size={10} className="text-concrete/60 shrink-0" />}
              {!isActive && hasActiveDescendant && (
                <span className="w-1.5 h-1.5 rounded-full bg-brand-accent/70 shrink-0" />
              )}
            </div>

            {/* Compteur de notes (toujours visible, à droite du nom) */}
            {notes.length > 0 && (
              <span className="text-[9px] font-bold text-concrete/40 px-1.5 border border-border/40 rounded-full group-hover/item:text-concrete transition-colors shrink-0 ms-auto">
                {notes.length}
              </span>
            )}

            {/* Boutons d'action en absolu, toujours à droite */}
            <div className="absolute end-0 top-0 bottom-0 flex items-center gap-1 pe-1 ps-6 opacity-0 group-hover/item:opacity-100 transition-opacity bg-gradient-to-l from-[var(--color-paper,white)] dark:from-[var(--color-paper,#1a1a1a)] from-60% to-transparent">
              {isPinned && (
                <span className="text-brand-accent" title={t('notebook.pinnedFrozenTooltip')}>
                  <Pin size={9} className="opacity-70" />
                </span>
              )}
              <button
                onClick={(e) => { e.stopPropagation(); onAddSubNotebook() }}
                className="p-1 hover:bg-ink/10 rounded-md transition-all text-concrete hover:text-ink"
                title={t('notebook.createSubNotebook')}
              >
                <Plus size={10} />
              </button>
              <button
                onClick={(e) => { e.stopPropagation(); onRename() }}
                className="p-1 hover:bg-ink/10 rounded-md transition-all text-concrete hover:text-ink"
                title={t('notebook.rename')}
              >
                <Pencil size={10} />
              </button>
              <button
                onClick={(e) => { e.stopPropagation(); onDelete() }}
                className="p-1 hover:bg-rose-50 rounded-md transition-all text-concrete hover:text-rose-500"
                title={t('notebook.delete')}
              >
                <Trash2 size={10} />
              </button>
            </div>
          </motion.div>
        </div>
      </div>

      {/* Right-click context menu */}
      <AnimatePresence>
        {contextMenu && (
          <motion.div
            initial={{ opacity: 0, scale: 0.95 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.95 }}
            transition={{ duration: 0.1 }}
            style={{ position: 'fixed', top: contextMenu.y, left: contextMenu.x, zIndex: 9999 }}
            className="bg-card border border-border rounded-xl shadow-xl py-1.5 min-w-[180px] overflow-hidden"
            onClick={(e) => e.stopPropagation()}
          >
            <button
              onClick={() => { onTogglePin(); setContextMenu(null) }}
              className="w-full flex items-center gap-2.5 px-3 py-2 text-[12px] text-ink hover:bg-foreground/5 transition-colors"
            >
              {isPinned
                ? <><PinOff size={13} className="text-brand-accent" /><span>{t('sidebar.unfreezePinnedNotebook')}</span></>
                : <><Pin size={13} className="text-brand-accent" /><span>{t('sidebar.freezePinnedNotebook')}</span></>
              }
            </button>
            <div className="mx-3 my-1 border-t border-border/50" />
            <button
              onClick={() => { onAddSubNotebook(); setContextMenu(null) }}
              className="w-full flex items-center gap-2.5 px-3 py-2 text-[12px] text-ink hover:bg-foreground/5 transition-colors"
            >
              <Plus size={13} className="text-concrete" />
              <span>{t('sidebar.newSubNotebook')}</span>
            </button>
            <button
              onClick={() => { onRename(); setContextMenu(null) }}
              className="w-full flex items-center gap-2.5 px-3 py-2 text-[12px] text-ink hover:bg-foreground/5 transition-colors"
            >
              <Pencil size={13} className="text-concrete" />
              <span>{t('sidebar.renameNotebook')}</span>
            </button>
            <div className="mx-3 my-1 border-t border-border/50" />
            <button
              onClick={() => { onDelete(); setContextMenu(null) }}
              className="w-full flex items-center gap-2.5 px-3 py-2 text-[12px] text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/30 transition-colors"
            >
              <Trash2 size={13} />
              <span>{t('common.delete')}</span>
            </button>
          </motion.div>
        )}
      </AnimatePresence>

      <AnimatePresence initial={false}>
        {isExpanded && (
          <motion.div
            initial={{ height: 0, opacity: 0 }}
            animate={{ height: 'auto', opacity: 1 }}
            exit={{ height: 0, opacity: 0 }}
            transition={{ duration: 0.3, ease: [0.23, 1, 0.32, 1] }}
            className="overflow-hidden"
          >
            <div className="relative" style={{ marginInlineStart: `${(level + 1) * 16 + 10}px` }}>
              <div className="absolute start-[-6px] top-0 bottom-4 w-px bg-border/30" />

              <div className="space-y-0.5 py-1">
                {children}
                {isExpanded && notes.map(note => (
                  <NoteLink
                    key={note.id}
                    title={note.title}
                    isPinned={note.isPinned}
                    isActive={activeNoteId === note.id}
                    onClick={() => onNoteClick(note.id, carnet.id)}
                  />
                ))}
                {isExpanded && notes.length === 0 && !hasChildren && (
                  <p className="ps-6 py-1 text-[9px] italic text-muted-foreground/40 font-light">
                    {t('sidebar.notebookEmpty')}
                  </p>
                )}
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  )
}

export function Sidebar({ className, user }: { className?: string; user?: any }) {
  const pathname = usePathname()
  const searchParams = useSearchParams()
  const router = useRouter()
  const { t, language } = useLanguage()
  const isRtl = language === 'fa' || language === 'ar'
  const { notebooks, trashNotebook, updateNotebookOrderOptimistic, moveNotebookToParent, refreshNotebooks } = useNotebooks()
  const { open: openSearch } = useSearchModal()
  const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
  const [showAiWizard, setShowAiWizard] = useState(false)
  const [createParentId, setCreateParentId] = useState<string | null>(null)
  const [renamingNotebook, setRenamingNotebook] = useState<Notebook | null>(null)
  const [renameValue, setRenameValue] = useState('')
  const [isDark, setIsDark] = useState(false)
  const [isMobileOpen, setIsMobileOpen] = useState(false)

  // Écoute l'événement d'ouverture du menu mobile
  useEffect(() => {
    const handler = () => setIsMobileOpen(true)
    window.addEventListener('open-mobile-sidebar', handler)
    return () => window.removeEventListener('open-mobile-sidebar', handler)
  }, [])

  // Ferme la sidebar mobile lors d'une navigation
  useEffect(() => {
    setIsMobileOpen(false)
  }, [pathname, searchParams])

  useEffect(() => {
    setIsDark(document.documentElement.classList.contains('dark'))
  }, [])

  const toggleTheme = useCallback(() => {
    const next = !isDark
    setIsDark(next)
    const theme = next ? 'dark' : 'light'
    localStorage.setItem('theme-preference', theme)
    applyDocumentTheme(theme)
  }, [isDark])
  const [deletingNotebook, setDeletingNotebook] = useState<Notebook | null>(null)
  const [isDeleting, setIsDeleting] = useState(false)
  const [isRenaming, setIsRenaming] = useState(false)
  const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
  const [pinnedIds, setPinnedIds] = useState<Set<string>>(new Set())
  const [notebookNotes, setNotebookNotes] = useState<Record<string, { id: string; title: string; isPinned?: boolean }[]>>({})
  const [activeView, setActiveView] = useState<NavigationView>('notebooks')
  const [sortOrder, setSortOrder] = useState<SortOrder>('newest')
  const [showSortMenu, setShowSortMenu] = useState(false)
  const [notebookSearchQuery, setNotebookSearchQuery] = useState('')
  const [trashCount, setTrashCount] = useState(0)

  const [draggedId, setDraggedId] = useState<string | null>(null)
  // Ref stable pour éviter les stale closures dans les handlers de drop
  const draggedIdRef = useRef<string | null>(null)
  const [orderedNotebooks, setOrderedNotebooks] = useState<Notebook[]>([])
  const dragOverId = useRef<string | null>(null)
  const isSavingRef = useRef(false)

  const rootNotebooks = useMemo(() => orderedNotebooks.filter(nb => !nb.parentId), [orderedNotebooks])
  const childNotebooks = useMemo(() => {
    const map = new Map<string, Notebook[]>()
    for (const nb of orderedNotebooks) {
      if (nb.parentId) {
        const children = map.get(nb.parentId) || []
        children.push(nb)
        map.set(nb.parentId, children)
      }
    }
    return map
  }, [orderedNotebooks])

  const filteredNotebookIds = useMemo(() => {
    const q = notebookSearchQuery.trim().toLowerCase()
    if (!q) return null
    return new Set(
      notebooks
        .filter(
          (nb) =>
            nb.name.toLowerCase().includes(q) ||
            (notebookNotes[nb.id] || []).some((n) => n.title.toLowerCase().includes(q)),
        )
        .map((nb) => nb.id),
    )
  }, [notebooks, notebookNotes, notebookSearchQuery])

  const currentNotebookId = searchParams.get('notebook')
  const currentNoteId = searchParams.get('openNote')

  useEffect(() => {
    if (!currentNotebookId) return
    setExpandedIds(prev => {
      const next = new Set(prev)
      let id: string | null | undefined = currentNotebookId
      while (id) {
        next.add(id)
        const nb = notebooks.find(n => n.id === id)
        id = nb?.parentId ?? null
      }
      return next
    })
  }, [currentNotebookId, notebooks])

  const isInboxActive =
    pathname === '/home' &&
    !searchParams.get('notebook') &&
    !searchParams.get('labels') &&
    !searchParams.get('archived') &&
    !searchParams.get('trashed')

  useEffect(() => {
    if (pathname.startsWith('/brainstorm')) setActiveView('brainstorms')
    else if (pathname.startsWith('/agents') || pathname.startsWith('/lab')) setActiveView('agents')
    else if (pathname === '/insights') setActiveView('insights')
    else if (pathname.startsWith('/revision')) setActiveView('revision')
    else if (searchParams.get('reminders') === '1' && pathname === '/home') setActiveView('reminders')
    else if (pathname === '/home' || pathname.startsWith('/notes')) setActiveView('notebooks')
  }, [pathname, searchParams])

  const isRemindersRoute = pathname === '/home' && searchParams.get('reminders') === '1'
  const isSharedRoute = pathname === '/home' && searchParams.get('shared') === '1'
  const isImmersiveRoute = pathname === '/insights'

  // Sur /insights : sidebar visible par défaut, user peut le cacher via toggle
  const [userCollapsed, setUserCollapsed] = useState(false)
  useEffect(() => {
    if (isImmersiveRoute) {
      const stored = localStorage.getItem('insights-sidebar-collapsed')
      setUserCollapsed(stored === 'true')
    }
  }, [isImmersiveRoute])

  useEffect(() => {
    const onToggle = () => {
      setUserCollapsed(prev => {
        const next = !prev
        if (isImmersiveRoute) localStorage.setItem('insights-sidebar-collapsed', String(next))
        return next
      })
    }
    window.addEventListener('toggle-insights-sidebar', onToggle)
    return () => window.removeEventListener('toggle-insights-sidebar', onToggle)
  }, [isImmersiveRoute])
  const isNotebooksRoute =
    (pathname === '/home' || pathname.startsWith('/notes')) &&
    !pathname.startsWith('/settings') &&
    !isRemindersRoute &&
    !isSharedRoute

  const displayName = user?.name || user?.email || ''
  const initial = displayName ? displayName.charAt(0).toUpperCase() : '?'

  // Sorted list for the sort dropdown (not used directly when dragging)
  const sortedNotebooks = useMemo(() => {
    const arr = [...notebooks]
    if (sortOrder === 'manual') return arr.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
    if (sortOrder === 'alpha') return arr.sort((a, b) => a.name.localeCompare(b.name))
    if (sortOrder === 'newest') return arr.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
    if (sortOrder === 'oldest') return arr.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
    return arr
  }, [notebooks, sortOrder])

  // Sync orderedNotebooks from server ONLY when not in the middle of a drag save
  useEffect(() => {
    if (isSavingRef.current) return
    setOrderedNotebooks(sortedNotebooks)
  }, [sortedNotebooks])

  useEffect(() => {
    let cancelled = false
    getTrashCount().then(count => { if (!cancelled) setTrashCount(count) })
    return () => { cancelled = true }
  }, [])

  const notebookIdsKey = useMemo(() => notebooks.map(nb => nb.id).sort().join(','), [notebooks])

  useEffect(() => {
    if (!notebookIdsKey) return
    let cancelled = false
    const load = async () => {
      const mappedEntries = await Promise.all(
        notebooks.map(async (nb: Notebook) => {
          const notes = await getAllNotes(false, nb.id)
          const mapped = notes.map((n: Note) => ({
            id: n.id,
            title: getNoteDisplayTitle(n, t('notes.untitled')),
            isPinned: n.isPinned,
          }))
          return [nb.id, mapped] as const
        })
      )
      if (cancelled) return
      setNotebookNotes(Object.fromEntries(mappedEntries))
    }
    load()
    return () => { cancelled = true }
  }, [notebookIdsKey, t])

  useEffect(() => {
    const onNoteChange = (e: Event) => {
      const detail = (e as CustomEvent<NoteChangeEvent>).detail
      if (detail.type === 'deleted') {
        setNotebookNotes((prev) => {
          const next = { ...prev }
          for (const key of Object.keys(next)) {
            next[key] = next[key].filter((n) => n.id !== detail.noteId)
          }
          return next
        })
        setTrashCount((count) => count + 1)
        return
      }
      if (detail.type === 'created' && detail.note.notebookId) {
        const nbId = detail.note.notebookId
        const title = getNoteDisplayTitle(detail.note, t('notes.untitled'))
        setNotebookNotes((prev) => {
          const list = prev[nbId] || []
          if (list.some((n) => n.id === detail.note.id)) return prev
          return {
            ...prev,
            [nbId]: [{ id: detail.note.id, title, isPinned: detail.note.isPinned }, ...list],
          }
        })
        return
      }
      if (detail.type === 'updated') {
        const note = detail.note
        const title = getNoteDisplayTitle(note, t('notes.untitled'))
        setNotebookNotes((prev) => {
          const next: Record<string, { id: string; title: string; isPinned?: boolean }[]> = {}
          for (const [key, list] of Object.entries(prev)) {
            const filtered = list.filter((n) => n.id !== note.id)
            if (filtered.length > 0) next[key] = filtered
          }
          if (note.notebookId) {
            next[note.notebookId] = [
              { id: note.id, title, isPinned: note.isPinned },
              ...(next[note.notebookId] || []),
            ]
          }
          return next
        })
      }
    }
    window.addEventListener(NOTE_CHANGE_EVENT, onNoteChange)
    return () => window.removeEventListener(NOTE_CHANGE_EVENT, onNoteChange)
  }, [t])

  const handleCarnetClick = (notebookId: string) => {
    const params = new URLSearchParams()
    params.set('notebook', notebookId)
    params.set('forceList', '1')
    router.push(`/home?${params.toString()}`)
  }

  const handleRemindersClick = () => {
    setActiveView('reminders')
    router.push('/home?reminders=1&forceList=1')
  }

  const handleReminderNoteClick = (noteId: string, notebookId: string | null) => {
    const params = new URLSearchParams()
    params.set('reminders', '1')
    if (notebookId) params.set('notebook', notebookId)
    params.set('openNote', noteId)
    router.push(`/home?${params.toString()}`)
  }

  const handleInboxClick = () => {
    router.push('/home?forceList=1')
  }

  const handleDailyNoteClick = async () => {
    try {
      const res = await fetch('/api/notes/daily')
      const data = await res.json()
      if (data.success && data.note) {
        const params = new URLSearchParams()
        if (data.note.notebookId) params.set('notebook', data.note.notebookId)
        params.set('openNote', data.note.id)
        router.push(`/home?${params.toString()}`)
      }
    } catch {
      toast.error(t('sidebar.dailyNoteError') || 'Impossible d\'ouvrir la note du jour')
    }
  }

  const handleNoteClick = (noteId: string, notebookId: string) => {
    const params = new URLSearchParams(searchParams.toString())
    params.set('notebook', notebookId)
    params.set('openNote', noteId)
    params.delete('forceList')
    router.push(`/home?${params.toString()}`)
  }

  // ── Drag state ──
  const [dropTarget, setDropTarget] = useState<string | null>(null)
  const [dropAction, setDropAction] = useState<'into' | 'before' | 'after' | null>(null)

  const handleDragStart = useCallback((e: React.DragEvent, notebookId: string) => {
    // Stocker dans le ref ET dans le state
    // Le ref est toujours frais dans les handlers async (pas de stale closure)
    draggedIdRef.current = notebookId
    setDraggedId(notebookId)
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('text/plain', notebookId)
  }, [])

  const handleDragEnd = useCallback(() => {
    draggedIdRef.current = null
    setDraggedId(null)
    dragOverId.current = null
    isSavingRef.current = false
    setDropTarget(null)
    setDropAction(null)
    setOrderedNotebooks(sortedNotebooks)
  }, [sortedNotebooks])

  const handleDropOnNotebook = useCallback(async (e: React.DragEvent, targetId: string, action: 'into' | 'before' | 'after') => {
    e.preventDefault()
    e.stopPropagation()
    const dragId = draggedIdRef.current || e.dataTransfer.getData('text/plain')
    draggedIdRef.current = null
    setDraggedId(null)
    setDropTarget(null)
    setDropAction(null)
    dragOverId.current = null
    if (!dragId || dragId === targetId) return
    try {
      if (action === 'into') {
        await moveNotebookToParent(dragId, targetId)
      } else {
        // before/after : placer au même niveau que la cible et réordonner
        const target = orderedNotebooks.find(nb => nb.id === targetId)
        const newParentId = target?.parentId ?? null

        // 1. Mettre à jour le parentId si nécessaire
        const dragged = orderedNotebooks.find(nb => nb.id === dragId)
        if (dragged?.parentId !== newParentId) {
          await moveNotebookToParent(dragId, newParentId)
        }

        // 2. Recalculer l'ordre des siblings
        const siblings = orderedNotebooks
          .filter(nb => (nb.parentId ?? null) === newParentId && nb.id !== dragId)
        const targetIdx = siblings.findIndex(nb => nb.id === targetId)
        const insertAt = action === 'before' ? targetIdx : targetIdx + 1
        siblings.splice(insertAt, 0, { id: dragId } as typeof siblings[0])
        // Passer en tri manuel pour que l'ordre soit respecté visuellement
        setSortOrder('manual')
        await updateNotebookOrderOptimistic(siblings.map(nb => nb.id))
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : t('sidebar.moveFailed'))
      setOrderedNotebooks(sortedNotebooks)
    }
  }, [moveNotebookToParent, updateNotebookOrderOptimistic, orderedNotebooks, sortedNotebooks, t])

  const handleDropToRoot = useCallback(async (e: React.DragEvent) => {
    e.preventDefault()
    // Lire depuis le ref (toujours à jour, pas de stale closure)
    const dragId = draggedIdRef.current || e.dataTransfer.getData('text/plain')
    // Nettoyer APRÈS avoir lu la valeur
    draggedIdRef.current = null
    setDraggedId(null)
    setDropTarget(null)
    setDropAction(null)
    dragOverId.current = null
    if (!dragId) return
    try {
      await moveNotebookToParent(dragId, null)
    } catch (err) {
      toast.error(err instanceof Error ? err.message : t('sidebar.moveFailed'))
      setOrderedNotebooks(sortedNotebooks)
    }
  }, [moveNotebookToParent, sortedNotebooks, t])

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

  const toggleExpand = useCallback((id: string) => {
    setExpandedIds(prev => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })
  }, [])

  // Load pinned notebooks from localStorage on mount
  useEffect(() => {
    try {
      const stored = localStorage.getItem('momento-pinned-notebooks')
      if (stored) setPinnedIds(new Set(JSON.parse(stored)))
    } catch {}
  }, [])

  const togglePin = useCallback((id: string) => {
    setPinnedIds(prev => {
      const next = new Set(prev)
      if (next.has(id)) {
        next.delete(id)
        // Also collapse the notebook when unpinning
        setExpandedIds(e => { const ne = new Set(e); ne.delete(id); return ne })
      } else {
        next.add(id)
        // Ensure it's also expanded when pinned
        setExpandedIds(e => { const ne = new Set(e); ne.add(id); return ne })
      }
      try { localStorage.setItem('momento-pinned-notebooks', JSON.stringify([...next])) } catch {}
      return next
    })
  }, [])

  const handleStartRename = useCallback((notebook: Notebook) => {
    setRenamingNotebook(notebook)
    setRenameValue(notebook.name)
  }, [])

  const handleConfirmRename = useCallback(async () => {
    if (!renamingNotebook || !renameValue.trim()) return
    setIsRenaming(true)
    try {
      const res = await fetch(`/api/notebooks/${renamingNotebook.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name: renameValue.trim() }),
      })
      if (!res.ok) throw new Error('Rename failed')
      setRenamingNotebook(null)
      setRenameValue('')
      await refreshNotebooks()
    } catch (err) {
      console.error('Rename failed:', err)
    } finally {
      setIsRenaming(false)
    }
  }, [renamingNotebook, renameValue, refreshNotebooks])

  const getDescendantIds = useCallback((notebookId: string): string[] => {
    const ids: string[] = []
    const children = childNotebooks.get(notebookId) || []
    for (const child of children) {
      ids.push(child.id)
      ids.push(...getDescendantIds(child.id))
    }
    return ids
  }, [childNotebooks])

  const handleConfirmDelete = useCallback(async () => {
    if (!deletingNotebook) return
    setIsDeleting(true)
    try {
      await trashNotebook(deletingNotebook.id)
      setDeletingNotebook(null)
      if (currentNotebookId === deletingNotebook.id) {
        router.push('/home')
      }
    } catch (err) {
      console.error('Trash failed:', err)
    } finally {
      setIsDeleting(false)
    }
  }, [deletingNotebook, trashNotebook, currentNotebookId, router])

  const renderCarnetTree = useCallback((parentId: string | undefined, level: number): React.ReactNode => {
    const items = (parentId === undefined
      ? rootNotebooks
      : (childNotebooks.get(parentId) || [])
    ).filter((notebook) => !filteredNotebookIds || filteredNotebookIds.has(notebook.id))

    return items.map((notebook: Notebook) => {
      const isActive = currentNotebookId === notebook.id
      const notes = notebookNotes[notebook.id] || []
      const isDragging = draggedId === notebook.id
      const children = childNotebooks.get(notebook.id) || []
      const hasDescendant = (nid: string): boolean => {
        const desc = childNotebooks.get(nid) || []
        return desc.some(c => currentNotebookId === c.id || hasDescendant(c.id))
      }
      const hasActiveDescendant = hasDescendant(notebook.id)
      // A notebook stays expanded if: manually expanded OR is pinned
      const isExpanded = expandedIds.has(notebook.id) || pinnedIds.has(notebook.id)

      return (
        <motion.div key={notebook.id}>
          <div
            draggable
            onDragStart={(e) => {
              e.stopPropagation()
              handleDragStart(e, notebook.id)
            }}
            onDragEnd={handleDragEnd}
            onDragOver={(e) => {
              e.preventDefault()
              e.stopPropagation()
              if (!draggedId || draggedId === notebook.id) return
              e.dataTransfer.dropEffect = 'move'
              const rect = e.currentTarget.getBoundingClientRect()
              const y = e.clientY - rect.top
              const pct = y / rect.height
              setDropTarget(notebook.id)
              if (pct < 0.3) setDropAction('before')
              else if (pct > 0.7) setDropAction('after')
              else setDropAction('into')
            }}
            onDragLeave={(e) => {
              if (e.currentTarget.contains(e.relatedTarget as Node)) return
              if (dropTarget === notebook.id) {
                setDropTarget(null)
                setDropAction(null)
              }
            }}
            onDrop={(e) => {
              e.preventDefault()
              e.stopPropagation()
              const action = (dropTarget === notebook.id ? dropAction : null) ?? 'into'
              handleDropOnNotebook(e, notebook.id, action as 'into' | 'before' | 'after')
            }}
            className={cn(
              'relative rounded-lg transition-colors',
              dropTarget === notebook.id && dropAction === 'into' && draggedId && draggedId !== notebook.id
                && 'bg-brand-accent/10 ring-1 ring-brand-accent/30',
              isDragging && 'opacity-50'
            )}
          >
            {/* Indicateur "déposer avant" */}
            {dropTarget === notebook.id && dropAction === 'before' && draggedId && draggedId !== notebook.id && (
              <div className="absolute -top-0.5 start-0 end-0 h-0.5 bg-brand-accent rounded-full z-10 pointer-events-none" />
            )}
            {/* Indicateur "déposer après" */}
            {dropTarget === notebook.id && dropAction === 'after' && draggedId && draggedId !== notebook.id && (
              <div className="absolute -bottom-0.5 start-0 end-0 h-0.5 bg-brand-accent rounded-full z-10 pointer-events-none" />
            )}
            <SidebarCarnetItem
              carnet={{
                id: notebook.id,
                name: notebook.name,
                initial: notebook.name.charAt(0).toUpperCase(),
                isPrivate: notebook.isPrivate,
              }}
              isActive={isActive}
              notes={notes}
              activeNoteId={currentNoteId}
              onCarnetClick={() => {
                if (currentNotebookId === notebook.id) {
                  toggleExpand(notebook.id)
                } else {
                  handleCarnetClick(notebook.id)
                }
              }}
              onNoteClick={handleNoteClick}
              onAddSubNotebook={() => {
                setCreateParentId(notebook.id)
                setIsCreateDialogOpen(true)
                if (!expandedIds.has(notebook.id)) toggleExpand(notebook.id)
              }}
              onRename={() => handleStartRename(notebook)}
              onDelete={() => setDeletingNotebook(notebook)}
              onTogglePin={() => togglePin(notebook.id)}
              isPinned={pinnedIds.has(notebook.id)}
              isDragging={isDragging}
              level={level}
              isExpanded={isExpanded}
              toggleExpand={() => toggleExpand(notebook.id)}
              hasChildNotebooks={children.length > 0}
              hasActiveDescendant={hasActiveDescendant}
            />
          </div>
          {isExpanded && renderCarnetTree(notebook.id, level + 1)}
        </motion.div>
      )
    })
  }, [rootNotebooks, childNotebooks, filteredNotebookIds, currentNotebookId, currentNoteId, notebookNotes, draggedId, dropTarget, dropAction, expandedIds, pinnedIds, toggleExpand, handleCarnetClick, handleNoteClick, handleDragStart, handleDragEnd, handleDropOnNotebook, handleStartRename])

  return (
    <>
      {/* Overlay mobile */}
      <AnimatePresence>
        {isMobileOpen && (
          <motion.div
            key="sidebar-backdrop"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: 0.2 }}
            className="fixed inset-0 bg-black/40 backdrop-blur-sm z-[60] md:hidden"
            onClick={() => setIsMobileOpen(false)}
          />
        )}
      </AnimatePresence>

      <aside
        className={cn(
          isImmersiveRoute && userCollapsed
            ? 'fixed inset-y-0 start-0 z-[70]'
            : 'fixed inset-y-0 start-0 z-[70] md:relative md:z-auto',
          'h-full min-h-0 w-80 xl:w-[22rem] 2xl:w-[26rem] shrink-0 flex flex-row overflow-hidden',
          'transition-transform duration-300 ease-in-out',
          isImmersiveRoute && userCollapsed
            ? '-translate-x-full'
            : (isMobileOpen ? 'translate-x-0 shadow-2xl' : '-translate-x-full md:translate-x-0'),
          'border-e border-border/40 bg-white/95 md:bg-white/30 backdrop-blur-md sidebar-shadow dark:border-white/6 dark:bg-[#151515] dark:backdrop-blur-none',
          className
        )}
      >
        {/* ── Column 1 : Rail d'icônes (54px) — inspiré du prototype ── */}
        <div className="w-[54px] border-e border-border/40 bg-[#FAF9F5] dark:bg-[#0E0E0E] flex flex-col items-center justify-between py-5 shrink-0 select-none">

          {/* Top : Logo + navigation */}
          <div className="flex flex-col items-center gap-[18px] w-full">
            {/* Logo avec dropdown profil */}
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <div className="w-9 h-9 bg-brand-accent hover:rotate-6 active:scale-95 flex items-center justify-center rounded-xl shadow-md transition-all cursor-pointer mb-2">
                  <span className="text-white font-serif font-bold text-sm">M</span>
                </div>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="start" className="w-52 bg-popover border-border ms-2">
                <DropdownMenuItem asChild>
                  <Link href="/settings/profile" className="flex items-center gap-2 cursor-pointer">
                    <User className="h-4 w-4" />
                    {t('sidebar.profile')}
                  </Link>
                </DropdownMenuItem>
                {(user as { role?: string } | undefined)?.role === 'ADMIN' && (
                  <DropdownMenuItem asChild>
                    <a href="/admin" className="flex items-center gap-2 cursor-pointer">
                      <Shield className="h-4 w-4" />
                      {t('nav.adminDashboard')}
                    </a>
                  </DropdownMenuItem>
                )}
                <DropdownMenuSeparator />
                <DropdownMenuItem
                  className="text-destructive focus:text-destructive"
                  onClick={() => performSignOut('/login')}
                >
                  <LogOut className="h-4 w-4 me-2" />
                  {t('sidebar.signOut')}
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>

            {/* Boutons de navigation principaux */}
            <div className="flex flex-col gap-2 w-full px-1.5">
              {([
                {
                  id: 'notebooks',
                  icon: BookOpen,
                  label: t('nav.notebooks'),
                  onClick: () => {
                    setActiveView('notebooks')
                    router.push('/home?forceList=1')
                  },
                  isActive: isNotebooksRoute,
                },
                {
                  id: 'insights',
                  icon: Sparkles,
                  label: t('nav.insights'),
                  onClick: () => {
                    setActiveView('insights')
                    router.push('/insights')
                  },
                  isActive: pathname === '/insights',
                },
                {
                  id: 'revision',
                  icon: GraduationCap,
                  label: t('nav.revision'),
                  onClick: () => {
                    setActiveView('revision')
                    router.push('/revision')
                  },
                  isActive: pathname.startsWith('/revision'),
                },
                {
                  id: 'agents',
                  icon: Bot,
                  label: t('agents.intelligenceOS') || 'Intelligence IA',
                  onClick: () => {
                    setActiveView('agents')
                    router.push('/agents')
                  },
                  isActive: pathname.startsWith('/agents') || pathname.startsWith('/lab'),
                },
                {
                  id: 'reminders',
                  icon: Bell,
                  label: t('sidebar.reminders'),
                  onClick: handleRemindersClick,
                  isActive: isRemindersRoute,
                },
              ] as { id: string; icon: React.FC<{ size?: number }>; label: string; onClick: () => void; isActive: boolean }[]).map(item => (
                <button
                  key={item.id}
                  type="button"
                  aria-label={item.label}
                  aria-current={item.isActive ? 'page' : undefined}
                  onClick={item.onClick}
                  className={cn(
                    'w-9 h-9 rounded-lg flex items-center justify-center transition-all relative group',
                    item.isActive
                      ? 'bg-brand-accent/10 text-brand-accent border border-brand-accent/25'
                      : 'text-concrete hover:text-ink dark:hover:text-white hover:bg-black/[0.04] dark:hover:bg-white/[0.04]'
                  )}
                >
                  {item.isActive && (
                    <div className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-brand-accent rounded-r-full" />
                  )}
                  <item.icon size={16} />
                  <span
                    aria-hidden="true"
                    className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider"
                  >
                    {item.label}
                  </span>
                </button>
              ))}
            </div>
          </div>

          {/* Bottom : utilitaires */}
          <div className="flex flex-col gap-1.5 w-full px-1.5 items-center">
            <NotificationPanel />

            <Link
              href="/trash"
              className={cn(
                'w-9 h-9 rounded-lg flex items-center justify-center transition-all relative group',
                pathname === '/trash'
                  ? 'bg-rose-500/10 text-rose-500 border border-rose-500/25'
                  : 'text-concrete hover:text-rose-500 hover:bg-rose-500/5'
              )}
            >
              {pathname === '/trash' && <div className="absolute left-0 top-1/2 -translate-y-1/2 w-[3px] h-4 bg-rose-500 rounded-r-full" />}
              <Trash2 size={16} />
              {trashCount > 0 && (
                <span className="absolute top-1.5 right-1.5 w-1.5 h-1.5 bg-rose-500 rounded-full border-2 border-[#FAF9F5] dark:border-[#0E0E0E]" />
              )}
              <span className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider">
                {t('sidebar.trash')}
              </span>
            </Link>

            <Link
              href="/home?shared=1&forceList=1"
              className={cn(
                'w-9 h-9 rounded-lg flex items-center justify-center transition-all relative group',
                searchParams.get('shared') === '1' && pathname === '/home'
                  ? 'bg-sky-500/10 text-sky-500 border border-sky-500/25'
                  : 'text-concrete hover:text-sky-500 hover:bg-sky-500/5'
              )}
            >
              <Users size={16} />
              <span className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider">
                {t('sidebar.sharedWithMe')}
              </span>
            </Link>

            <button
              onClick={openSearch}
              className="w-9 h-9 rounded-lg flex items-center justify-center text-concrete hover:text-ink dark:hover:text-white hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-all relative group"
              title="Ctrl+K"
            >
              <Search size={15} />
              <span className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider">
                Recherche (Ctrl+K)
              </span>
            </button>

            <button
              onClick={toggleTheme}
              className="w-9 h-9 rounded-lg flex items-center justify-center text-concrete hover:text-ink dark:hover:text-white hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-all relative group"
            >
              {isDark ? <Sun size={15} /> : <Moon size={15} />}
              <span className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider">
                {isDark ? 'Mode clair' : 'Mode sombre'}
              </span>
            </button>

            <Link
              href="/settings"
              className={cn(
                'w-9 h-9 rounded-lg flex items-center justify-center transition-all relative group',
                pathname.startsWith('/settings')
                  ? 'bg-brand-accent/10 text-brand-accent border border-brand-accent/25'
                  : 'text-concrete hover:text-ink dark:hover:text-white hover:bg-black/[0.04] dark:hover:bg-white/[0.04]'
              )}
            >
              {pathname.startsWith('/settings') && <div className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-brand-accent rounded-r-full" />}
              <Settings size={15} />
              <span className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider">
                {t('nav.settings')}
              </span>
            </Link>

            <button
              onClick={() => performSignOut('/login')}
              className="w-9 h-9 rounded-lg flex items-center justify-center text-concrete hover:text-red-500 hover:bg-rose-500/5 transition-all relative group"
            >
              <LogOut size={14} />
              <span className="absolute left-[50px] top-1/2 -translate-y-1/2 bg-ink dark:bg-white dark:text-ink text-paper text-[9px] font-bold py-1 px-2 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap z-50 pointer-events-none shadow-md uppercase tracking-wider">
                {t('sidebar.signOut')}
              </span>
            </button>
          </div>
        </div>

        {/* ── Column 2 : Panneau de contenu dynamique ── */}
        <div className="flex-1 h-full flex flex-col overflow-hidden bg-[#FCFCFA] dark:bg-[#111111]">

        {/* ── Scrollable content ── */}
        <div className="flex-1 overflow-y-auto space-y-6 -mx-0 custom-scrollbar pb-4">

          <AnimatePresence mode="wait">
            {activeView === 'notebooks' ? (
              <motion.div
                key="notebooks"
                initial={{ opacity: 0, x: isRtl ? 10 : -10 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: isRtl ? -10 : 10 }}
                transition={{ duration: 0.2 }}
                className="flex flex-col flex-1 min-h-0 overflow-hidden"
              >
                <div className="px-4 pt-4 shrink-0">
                  <div className="flex items-center justify-between mb-4">
                    <div className="flex items-center gap-1.5">
                      <BookMarked size={14} className="text-brand-accent" />
                      <h3 className="text-xs font-black tracking-widest uppercase text-ink dark:text-dark-ink">
                        {t('sidebar.documents')}
                      </h3>
                    </div>
                    <div className="flex items-center gap-0.5">
                      <button
                        type="button"
                        onClick={() => {
                          setCreateParentId(null)
                          setIsCreateDialogOpen(true)
                        }}
                        className="p-1 hover:bg-black/5 dark:hover:bg-white/5 rounded transition-all text-concrete hover:text-ink"
                        title={t('notebook.create')}
                      >
                        <Plus size={15} />
                      </button>
                      <button
                        type="button"
                        onClick={() => setShowAiWizard(true)}
                        className="p-1 hover:bg-brand-accent/10 rounded transition-all text-concrete hover:text-brand-accent"
                        title={t('wizard.title')}
                      >
                        <Sparkles size={14} />
                      </button>
                      <div className="relative">
                        <button
                          type="button"
                          onClick={() => setShowSortMenu((s) => !s)}
                          className="p-1 hover:bg-black/5 dark:hover:bg-white/5 rounded transition-all text-concrete hover:text-ink"
                          title={t('sidebar.sortOrder')}
                        >
                          <ArrowUpDown size={13} />
                        </button>
                        <AnimatePresence>
                          {showSortMenu && (
                            <motion.div
                              initial={{ opacity: 0, scale: 0.9, y: -4 }}
                              animate={{ opacity: 1, scale: 1, y: 0 }}
                              exit={{ opacity: 0, scale: 0.9, y: -4 }}
                              className="absolute end-0 top-full mt-1 bg-card border border-border rounded-xl shadow-lg z-50 py-1 min-w-[140px]"
                            >
                              {(['newest', 'oldest', 'alpha', 'manual'] as SortOrder[]).map((order) => (
                                <button
                                  key={order}
                                  type="button"
                                  onClick={() => {
                                    setSortOrder(order)
                                    setShowSortMenu(false)
                                  }}
                                  className={cn(
                                    'w-full text-start px-4 py-2 text-[12px] transition-colors',
                                    sortOrder === order
                                      ? 'font-bold text-foreground'
                                      : 'text-muted-foreground hover:text-foreground hover:bg-muted/40',
                                  )}
                                >
                                  {sortLabels[order]}
                                </button>
                              ))}
                            </motion.div>
                          )}
                        </AnimatePresence>
                      </div>
                    </div>
                  </div>

                  <div className="relative mb-4">
                    <input
                      type="text"
                      value={notebookSearchQuery}
                      onChange={(e) => setNotebookSearchQuery(e.target.value)}
                      placeholder={t('sidebar.searchNotebooksPlaceholder')}
                      className="w-full text-[11px] ps-7 pe-8 py-1.5 rounded-lg border border-border/60 bg-white/70 dark:bg-zinc-800 placeholder-concrete/50 outline-none focus:border-brand-accent transition-colors text-ink dark:text-dark-ink"
                    />
                    <Search
                      size={11}
                      className="absolute start-2.5 top-1/2 -translate-y-1/2 text-concrete opacity-60 pointer-events-none"
                    />
                    {notebookSearchQuery && (
                      <button
                        type="button"
                        onClick={() => setNotebookSearchQuery('')}
                        className="absolute end-2.5 top-1/2 -translate-y-1/2 text-[9px] uppercase font-bold text-concrete hover:text-ink"
                        aria-label={t('sidebar.clearSearch')}
                      >
                        X
                      </button>
                    )}
                  </div>
                </div>

                <div className="flex-1 overflow-y-auto custom-scrollbar min-h-0 px-4 pb-4">
                  <button
                    type="button"
                    onClick={handleInboxClick}
                    className={cn('sidebar-inbox-item', isInboxActive && 'active')}
                  >
                    <div
                      className={cn(
                        'w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium border shrink-0',
                        isInboxActive
                          ? 'bg-brand-accent text-white border-brand-accent'
                          : 'bg-paper dark:bg-white/5 text-muted-ink border-border group-hover:border-brand-accent/20',
                      )}
                    >
                      <Inbox size={14} />
                    </div>
                    <span
                      className={cn(
                        'text-[13px] font-medium truncate',
                        isInboxActive ? 'text-ink' : 'text-muted-ink',
                      )}
                    >
                      {t('sidebar.inbox')}
                    </span>
                  </button>

                  <button
                    type="button"
                    onClick={handleDailyNoteClick}
                    className="sidebar-inbox-item"
                    title={t('sidebar.dailyNoteTooltip') || "Ouvrir ou créer la note personnelle pour le journal d'aujourd'hui"}
                  >
                    <div className="w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium border shrink-0 bg-paper dark:bg-white/5 text-muted-ink border-border">
                      <CalendarDays size={14} />
                    </div>
                    <span className="text-[13px] font-medium truncate text-muted-ink">
                      {t('sidebar.dailyNote') || 'Note du jour'}
                    </span>
                  </button>

                  <div className="my-3 h-px bg-border/40" />

                  {/* Zone de dépôt racine — toujours présente pour éviter tout décalage DOM */}
                  <div
                    className={`mb-2 h-10 rounded-lg border-2 border-dashed flex items-center justify-center gap-2 text-[11px] font-medium transition-all duration-150 ${
                      draggedId
                        ? 'border-brand-accent/40 bg-brand-accent/5 text-brand-accent/70 opacity-100 cursor-copy pointer-events-auto'
                        : 'border-transparent bg-transparent text-transparent opacity-0 pointer-events-none'
                    }`}
                    onDrop={handleDropToRoot}
                    onDragOver={(e) => { e.preventDefault(); e.stopPropagation() }}
                    onDragEnter={(e) => { e.preventDefault() }}
                  >
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
                    {t('sidebar.dropToRoot')}
                  </div>

                  <div
                    className="space-y-0.5 min-h-[60px]"
                    onDrop={handleDropToRoot}
                    onDragOver={(e) => e.preventDefault()}
                  >
                    {renderCarnetTree(undefined, 0)}
                  </div>
                </div>
              </motion.div>
            ) : activeView === 'insights' ? (
              <motion.div
                key="insights"
                initial={{ opacity: 0, x: isRtl ? 10 : -10 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: isRtl ? -10 : 10 }}
                transition={{ duration: 0.2 }}
                className="px-4 pt-4 space-y-4"
              >
                <div className="flex items-center gap-1.5">
                  <Sparkles size={14} className="text-brand-accent" />
                  <h3 className="text-xs font-black tracking-widest uppercase text-ink dark:text-dark-ink">
                    {t('nav.insights')}
                  </h3>
                </div>
                <p className="text-[12px] leading-relaxed text-muted-foreground">
                  {t('sidebar.insightsPanelBody')}
                </p>
                <button
                  type="button"
                  onClick={() => router.push('/home')}
                  className="w-full flex items-center gap-2 px-3 py-2.5 rounded-xl border border-border/40 bg-white/60 dark:bg-zinc-800/40 hover:border-brand-accent/30 hover:bg-brand-accent/5 transition-all text-[12px] font-medium text-foreground"
                >
                  <BookOpen size={14} className="text-brand-accent shrink-0" />
                  {t('sidebar.backToNotebooks')}
                </button>
              </motion.div>
            ) : activeView === 'revision' ? (
              <motion.div
                key="revision"
                initial={{ opacity: 0, x: isRtl ? 10 : -10 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: isRtl ? -10 : 10 }}
                transition={{ duration: 0.2 }}
                className="px-4 pt-4 space-y-4"
              >
                <div className="flex items-center gap-1.5">
                  <GraduationCap size={14} className="text-brand-accent" />
                  <h3 className="text-xs font-black tracking-widest uppercase text-ink dark:text-dark-ink">
                    {t('nav.revision')}
                  </h3>
                </div>
                <p className="text-[12px] leading-relaxed text-muted-foreground">
                  {t('sidebar.revisionPanelBody')}
                </p>
                <button
                  type="button"
                  onClick={() => router.push('/home')}
                  className="w-full flex items-center gap-2 px-3 py-2.5 rounded-xl border border-border/40 bg-white/60 dark:bg-zinc-800/40 hover:border-brand-accent/30 hover:bg-brand-accent/5 transition-all text-[12px] font-medium text-foreground"
                >
                  <BookOpen size={14} className="text-brand-accent shrink-0" />
                  {t('sidebar.backToNotebooks')}
                </button>
              </motion.div>
            ) : activeView === 'reminders' ? (
              <motion.div
                key="reminders"
                initial={{ opacity: 0, x: isRtl ? 10 : -10 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: isRtl ? -10 : 10 }}
                transition={{ duration: 0.2 }}
                className="flex flex-col min-h-0"
              >
                <div className="flex items-center gap-1.5 px-4 pt-4 mb-3 shrink-0">
                  <Bell size={14} className="text-brand-accent" />
                  <h3 className="text-xs font-black tracking-widest uppercase text-ink dark:text-dark-ink">
                    {t('sidebar.reminders')}
                  </h3>
                </div>
                <div className="flex-1 overflow-y-auto custom-scrollbar min-h-0">
                  <SidebarReminders onOpenNote={handleReminderNoteClick} />
                </div>
              </motion.div>
            ) : activeView === 'agents' ? (
              <motion.div
                key="agents"
                initial={{ opacity: 0, x: isRtl ? -10 : 10 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: isRtl ? 10 : -10 }}
                transition={{ duration: 0.2 }}
              >
                <p className="text-[10px] font-bold text-muted-foreground tracking-widest uppercase mb-4 px-4">
                  {t('agents.intelligenceOS')}
                </p>
                <div className="space-y-1">
                  {[
                    { id: 'agents', href: '/agents', label: t('agents.myAgents'), icon: Bot },
                    { id: 'lab', href: '/lab', label: t('nav.lab'), icon: FlaskConical },
                    { id: 'brainstorm', href: '/brainstorm', label: t('brainstorm.sessions'), icon: Sparkles },
                  ].map(item => {
                    const isActive = pathname.startsWith(item.href)
                    return (
                      <Link
                        key={item.id}
                        href={item.href}
                        className={cn(
                          'w-full flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 group',
                          isActive ? 'memento-active-nav' : 'text-muted-foreground hover:bg-foreground/5 hover:text-foreground'
                        )}
                      >
                        <div className={cn(
                          'w-8 h-8 rounded-full flex items-center justify-center border transition-colors shrink-0',
                          isActive
                            ? 'bg-brand-accent text-white border-brand-accent'
                            : 'bg-paper border-border group-hover:border-brand-accent/20'
                        )}>
                          <item.icon size={16} />
                        </div>
                        <span className="text-[13px] font-medium">{item.label}</span>
                      </Link>
                    )
                  })}
                </div>
              </motion.div>
            ) : (
              <motion.div
                key="brainstorms"
                initial={{ opacity: 0, x: isRtl ? -10 : 10 }}
                animate={{ opacity: 1, x: 0 }}
                exit={{ opacity: 0, x: isRtl ? 10 : -10 }}
                transition={{ duration: 0.2 }}
              >
                <div className="flex items-center justify-between px-4 mb-3">
                  <p className="text-[10px] font-bold text-muted-foreground tracking-widest uppercase">
                    {t('brainstorm.sessions')}
                  </p>
                  <button
                    onClick={() => router.push('/brainstorm')}
                    className="p-1 text-muted-foreground hover:text-brand-accent transition-colors rounded"
                    title={t('brainstorm.newBrainstorm')}
                  >
                    <Plus size={12} />
                  </button>
                </div>
                <SidebarBrainstorms />
              </motion.div>
            )}

          </AnimatePresence>
        </div>

        {/* ── Usage meter + starter badge en bas du panneau ── */}
        <div className="border-t border-border/20 px-3 py-3 mt-auto space-y-2">
          <StarterPackBadge />
          <UsageMeter />
        </div>

        </div>{/* fin colonne 2 */}
      </aside>

      <CreateNotebookDialog
        open={isCreateDialogOpen}
        onOpenChange={setIsCreateDialogOpen}
        parentNotebookId={createParentId}
      />

      {showAiWizard && (
        <AiNotebookWizard
          onClose={() => setShowAiWizard(false)}
          onComplete={(notebookId: string) => {
            setShowAiWizard(false)
            router.push(`/home?notebook=${notebookId}`)
          }}
        />
      )}

      {/* Rename Dialog */}
      <AnimatePresence>
        {renamingNotebook && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm"
            onClick={() => !isRenaming && setRenamingNotebook(null)}
          >
            <motion.div
              initial={{ scale: 0.9, opacity: 0, y: 10 }}
              animate={{ scale: 1, opacity: 1, y: 0 }}
              exit={{ scale: 0.9, opacity: 0, y: 10 }}
              transition={{ type: 'spring', stiffness: 300, damping: 25 }}
              className="bg-card border border-border rounded-2xl p-6 shadow-xl w-80"
              onClick={(e) => e.stopPropagation()}
            >
              <h3 className="text-sm font-semibold mb-4 font-memento-serif">
                {t('notebook.rename')}
              </h3>
              <input
                autoFocus
                value={renameValue}
                onChange={(e) => setRenameValue(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter') handleConfirmRename(); if (e.key === 'Escape') setRenamingNotebook(null) }}
                className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-transparent focus:outline-none focus:ring-2 focus:ring-foreground/20"
                placeholder={t('notebook.namePlaceholder')}
              />
              <div className="flex justify-end gap-2 mt-4">
                <button
                  onClick={() => setRenamingNotebook(null)}
                  disabled={isRenaming}
                  className="px-4 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors rounded-lg"
                >
                  {t('common.cancel')}
                </button>
                <button
                  onClick={handleConfirmRename}
                  disabled={isRenaming || !renameValue.trim()}
                  className="px-4 py-1.5 text-xs font-medium bg-foreground text-background rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50"
                >
                  {isRenaming ? '...' : t('notebook.confirmRename')}
                </button>
              </div>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Delete Confirmation */}
      <AnimatePresence>
        {deletingNotebook && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm"
            onClick={() => !isDeleting && setDeletingNotebook(null)}
          >
            <motion.div
              initial={{ scale: 0.9, opacity: 0, y: 10 }}
              animate={{ scale: 1, opacity: 1, y: 0 }}
              exit={{ scale: 0.9, opacity: 0, y: 10 }}
              transition={{ type: 'spring', stiffness: 300, damping: 25 }}
              className="bg-card border border-border rounded-2xl p-6 shadow-xl w-80"
              onClick={(e) => e.stopPropagation()}
            >
              <div className="w-10 h-10 rounded-full bg-red-50 border border-red-100 flex items-center justify-center mb-3">
                <Trash2 size={16} className="text-red-500" />
              </div>
              <h3 className="text-sm font-semibold mb-1 font-memento-serif">
                {t('notebook.trashTitle')}
              </h3>
              <p className="text-xs text-muted-foreground mb-4">
                {t('notebook.trashConfirm', { name: deletingNotebook.name }) || `Move "${deletingNotebook.name}" to trash? You can restore it within 30 days.`}
              </p>
              {getDescendantIds(deletingNotebook.id).length > 0 && (
                <p className="text-xs text-amber-600 mb-4 bg-amber-50 px-3 py-2 rounded-lg border border-amber-100">
                  {t('notebook.trashCascadeWarning', { count: getDescendantIds(deletingNotebook.id).length }) || `${getDescendantIds(deletingNotebook.id).length} sub-notebook(s) will also be moved to trash.`}
                </p>
              )}
              <div className="flex justify-end gap-2">
                <button
                  onClick={() => setDeletingNotebook(null)}
                  disabled={isDeleting}
                  className="px-4 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors rounded-lg"
                >
                  {t('common.cancel')}
                </button>
                <button
                  onClick={handleConfirmDelete}
                  disabled={isDeleting}
                  className="px-4 py-1.5 text-xs font-medium bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50"
                >
                  {isDeleting ? '...' : t('notebook.moveToTrash')}
                </button>
              </div>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  )
}
