'use client'

import { useCallback, useEffect, useMemo, useState, useTransition, useRef } from 'react'
import { useNoteRefreshOptional } from '@/context/NoteRefreshContext'
import {
  DndContext,
  type DragEndEvent,
  KeyboardSensor,
  PointerSensor,
  closestCenter,
  useSensor,
  useSensors,
} from '@dnd-kit/core'
import {
  SortableContext,
  arrayMove,
  verticalListSortingStrategy,
  sortableKeyboardCoordinates,
  useSortable,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { Note, NOTE_COLORS, NoteColor, NoteType } from '@/lib/types'
import { cn } from '@/lib/utils'
import { NoteInlineEditor } from '@/components/note-inline-editor'
import { useLanguage } from '@/lib/i18n'
import { getNoteDisplayTitle } from '@/lib/note-preview'
import {
  updateFullOrderWithoutRevalidation,
  createNote,
  deleteNote,
  updateNote,
  toggleArchive,
} from '@/app/actions/notes'
import { useNotebooks } from '@/context/notebooks-context'
import {
  GripVertical,
  ListChecks,
  Pin,
  PinOff,
  FileText,
  Plus,
  Loader2,
  Trash2,
  ListFilter,
  FolderInput,
  Archive,
  Share2,
  Check,
  Hash,
  History,
  PanelRightClose,
  PanelRightOpen,
  PanelLeftClose,
  PanelLeftOpen,
  Bell,
} from 'lucide-react'
import { Button } from '@/components/ui/button'
import { ReminderDialog } from '@/components/reminder-dialog'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuLabel,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from '@/components/ui/popover'
import { toast } from 'sonner'
import { format, type Locale } from 'date-fns'
import { fr } from 'date-fns/locale/fr'
import { enUS } from 'date-fns/locale/en-US'

interface NotesTabsViewProps {
  notes: Note[]
  onEdit?: (note: Note, readOnly?: boolean) => void
  currentNotebookId?: string | null
  noteHistoryMode?: 'manual' | 'auto'
  onOpenHistory?: (note: Note) => void
  onEnableHistory?: (noteId: string) => Promise<void>
  onNoteCreated?: (note: Note) => void
}

type SortOrder = 'date-desc' | 'date-asc' | 'title-asc' | 'title-desc'

// Color accent strip for each note
const COLOR_ACCENT: Record<NoteColor, string> = {
  default: 'bg-primary',
  red: 'bg-red-400',
  orange: 'bg-orange-400',
  yellow: 'bg-amber-400',
  green: 'bg-emerald-400',
  teal: 'bg-teal-400',
  blue: 'bg-slate-400',
  purple: 'bg-violet-400',
  pink: 'bg-fuchsia-400',
  gray: 'bg-gray-400',
}

// Background tint gradient for selected note panel
const COLOR_PANEL_BG: Record<NoteColor, string> = {
  default: 'from-background to-background',
  red: 'from-red-50/60 dark:from-red-950/20 to-background',
  orange: 'from-orange-50/60 dark:from-orange-950/20 to-background',
  yellow: 'from-amber-50/60 dark:from-amber-950/20 to-background',
  green: 'from-emerald-50/60 dark:from-emerald-950/20 to-background',
  teal: 'from-teal-50/60 dark:from-teal-950/20 to-background',
  blue: 'from-slate-50/60 dark:from-slate-950/20 to-background',
  purple: 'from-violet-50/60 dark:from-violet-950/20 to-background',
  pink: 'from-fuchsia-50/60 dark:from-fuchsia-950/20 to-background',
  gray: 'from-gray-50/60 dark:from-gray-900/20 to-background',
}

const COLOR_ICON: Record<NoteColor, string> = {
  default: 'text-primary',
  red: 'text-red-500',
  orange: 'text-orange-500',
  yellow: 'text-amber-500',
  green: 'text-emerald-500',
  teal: 'text-teal-500',
  blue: 'text-slate-600',
  purple: 'text-violet-500',
  pink: 'text-fuchsia-500',
  gray: 'text-gray-500',
}

function getColorKey(note: Note): NoteColor {
  return (typeof note.color === 'string' && note.color in NOTE_COLORS
    ? note.color
    : 'default') as NoteColor
}

function getDateLocale(language: string) {
  if (language === 'fr') return fr
  if (language === 'fa') return require('date-fns/locale').faIR
  return enUS
}

function formatNoteDate(date: Date | string, locale: Locale): string {
  const d = typeof date === 'string' ? new Date(date) : date
  return format(d, 'd MMM yyyy', { locale })
}

function countWords(content: string | null | undefined): number {
  if (!content) return 0
  return content.trim().split(/\s+/).filter(Boolean).length
}

// ─── Sortable List Item ───────────────────────────────────────────────────────

function SortableNoteListItem({
  note,
  selected,
  onSelect,
  onDelete,
  reorderLabel,
  deleteLabel,
  language,
  untitledLabel,
}: {
  note: Note
  selected: boolean
  onSelect: () => void
  onDelete: () => void
  reorderLabel: string
  deleteLabel: string
  language: string
  untitledLabel: string
}) {
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
    id: note.id,
  })

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    zIndex: isDragging ? 50 : undefined,
  }

  const ck = getColorKey(note)
  const title = getNoteDisplayTitle(note, untitledLabel)
  const snippet =
    note.type === 'checklist'
      ? (note.checkItems?.map((i) => i.text).join(' · ') || '').substring(0, 200)
      : (note.content || '').substring(0, 200)

  const dateLocale = getDateLocale(language)
  const dateStr = formatNoteDate(note.updatedAt, dateLocale)

  return (
    <div
      ref={setNodeRef}
      style={style}
      className={cn(
        'group relative flex cursor-pointer select-none items-stretch transition-all duration-150',
        'border-b border-border/50 last:border-b-0',
        selected
          ? 'bg-primary/[0.06] dark:bg-primary/10'
          : 'bg-background hover:bg-muted/40 dark:hover:bg-muted/20',
        isDragging && 'opacity-75 shadow-lg ring-1 ring-primary/20'
      )}
      onClick={onSelect}
      role="option"
      aria-selected={selected}
    >
      {/* Left accent bar — solid when selected, transparent otherwise */}
      <div
        className={cn(
          'w-[3px] shrink-0 rounded-r-full transition-all duration-200',
          selected ? COLOR_ACCENT[ck] : 'bg-transparent'
        )}
      />

      {/* Main card content */}
      <div className="min-w-0 flex-1 px-4 py-4">

        {/* Row 1: type icon + date */}
        <div className="mb-2 flex items-center justify-between gap-2">
          <div className="flex items-center gap-1.5">
            {note.type === 'checklist' ? (
              <ListChecks
                className={cn(
                  'h-3.5 w-3.5 shrink-0',
                  selected ? COLOR_ICON[ck] : 'text-muted-foreground/40'
                )}
              />
            ) : (
              <FileText
                className={cn(
                  'h-3.5 w-3.5 shrink-0',
                  selected ? COLOR_ICON[ck] : 'text-muted-foreground/40'
                )}
              />
            )}
            {note.isPinned && (
              <Pin className="h-3 w-3 shrink-0 fill-current text-primary/70" />
            )}
          </div>
          <span
            suppressHydrationWarning
            className={cn(
              'shrink-0 text-[11px] tabular-nums',
              selected ? 'text-muted-foreground' : 'text-muted-foreground/60'
            )}
          >
            {dateStr}
          </span>
        </div>

        {/* Row 2: title */}
        <p
          dir="auto"
          className={cn(
            'mb-1.5 text-[13.5px] leading-snug transition-colors',
            selected
              ? 'font-semibold text-foreground'
              : 'font-medium text-foreground/85 group-hover:text-foreground'
          )}
        >
          {title}
        </p>

        {/* Row 3: snippet */}
        {snippet && (
          <p dir="auto" className="line-clamp-2 text-[12px] leading-relaxed text-muted-foreground/60">
            {snippet}
          </p>
        )}

        {/* Row 4: label chips */}
        {Array.isArray(note.labels) && note.labels.length > 0 && (
          <div className="mt-2.5 flex flex-wrap gap-1.5">
            {note.labels.slice(0, 3).map((label) => (
              <span
                key={label}
                className={cn(
                  'inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-[10px] font-medium leading-none transition-colors',
                  selected
                    ? 'border-primary/25 text-primary/70'
                    : 'border-border text-muted-foreground/65 group-hover:border-border/80'
                )}
              >
                <span className="h-1 w-1 rounded-full bg-current opacity-60" />
                {label}
              </span>
            ))}
            {note.labels.length > 3 && (
              <span className="inline-flex items-center rounded-full border border-border/60 px-2 py-0.5 text-[10px] text-muted-foreground/50">
                +{note.labels.length - 3}
              </span>
            )}
          </div>
        )}
      </div>

      {/* Actions column: drag + delete on hover */}
      <div className="flex flex-col items-center justify-between py-3 pe-2 opacity-0 transition-opacity group-hover:opacity-100">
        <button
          type="button"
          className="cursor-grab p-1 text-muted-foreground/30 active:cursor-grabbing"
          aria-label={reorderLabel}
          {...attributes}
          {...listeners}
          onClick={(e) => e.stopPropagation()}
        >
          <GripVertical className="h-3.5 w-3.5" />
        </button>
        <button
          type="button"
          onClick={(e) => {
            e.stopPropagation()
            onDelete()
          }}
          className="p-1 text-muted-foreground/40 hover:text-destructive"
          aria-label={deleteLabel}
          title={deleteLabel}
        >
          <Trash2 className="h-3.5 w-3.5" />
        </button>
      </div>
    </div>
  )
}

// ─── Note Meta Sidebar ────────────────────────────────────────────────────────

function SidebarSection({ title }: { title: string }) {
  return (
    <div className="mb-3 flex items-center gap-2">
      <p className="text-[10px] font-black uppercase tracking-[0.12em] text-muted-foreground whitespace-nowrap">
        {title}
      </p>
      <div className="flex-1 h-px bg-border/60" />
    </div>
  )
}

function SidebarActionBtn({
  icon,
  label,
  onClick,
  disabled = false,
}: {
  icon: React.ReactNode
  label: string
  onClick: () => void
  disabled?: boolean
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      className={cn(
        "group flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-[13px] font-medium transition-all duration-150",
        disabled
          ? "cursor-not-allowed text-muted-foreground/60 opacity-70"
          : "text-foreground/70 hover:bg-stone-100/80 hover:text-stone-800 dark:hover:bg-white/5 dark:hover:text-stone-100"
      )}
    >
      <span
        className={cn(
          "transition-colors",
          disabled ? "text-muted-foreground/60" : "text-muted-foreground group-hover:text-stone-600 dark:group-hover:text-stone-300"
        )}
      >
        {icon}
      </span>
      {label}
    </button>
  )
}

function NoteMetaSidebar({
  note,
  onPinToggle,
  onArchive,
  onOpenHistory,
  onEnableHistory,
  onUpdateReminder,
}: {
  note: Note
  onPinToggle: (note: Note) => void
  onArchive: (note: Note) => void
  onOpenHistory?: (note: Note) => void
  onEnableHistory?: (noteId: string) => Promise<void>
  onUpdateReminder?: (noteId: string, reminder: Date | null) => void
}) {
  const { t } = useLanguage()
  const { notebooks, moveNoteToNotebookOptimistic } = useNotebooks()
  const [moveOpen, setMoveOpen] = useState(false)
  const [isMoving, setIsMoving] = useState(false)
  const [showReminder, setShowReminder] = useState(false)

  // t() returns the key itself when not found — use this wrapper for safe fallbacks
  const ts = (key: string, fallback: string) => {
    const v = t(key as Parameters<typeof t>[0])
    return v === key ? fallback : v
  }

  const wordCount = countWords(note.content)

  const noteTypeLabel =
    note.type === 'checklist'
      ? ts('notes.typeChecklist', 'Checklist')
      : note.isMarkdown
        ? ts('notes.typeMarkdown', 'Markdown')
        : ts('notes.typeText', 'Text')

  const handleMoveToNotebook = async (notebookId: string | null) => {
    setIsMoving(true)
    try {
      await moveNoteToNotebookOptimistic(note.id, notebookId)
      setMoveOpen(false)
      toast.success(ts('notebookSuggestion.movedToNotebook', 'Note déplacée'))
    } catch {
      toast.error(ts('notes.moveFailed', 'Déplacement échoué'))
    } finally {
      setIsMoving(false)
    }
  }

  const handleHistory = async () => {
    if (!note.historyEnabled) {
      if (!onEnableHistory) {
        toast.info(ts('notes.historyDisabledDesc', "L'historique est désactivé pour cette note."))
        return
      }
      try {
        await onEnableHistory(note.id)
        toast.success(ts('notes.historyEnabled', 'Historique activé'))
        onOpenHistory?.(note)
      } catch {
        toast.error(ts('general.error', 'Erreur'))
      }
      return
    }

    onOpenHistory?.(note)
  }

  return (
    <aside className="flex w-56 shrink-0 flex-col bg-muted border-l border-slate-300 border-t-2 border-t-primary/40 overflow-y-auto shadow-[-6px_0_16px_-4px_rgba(0,0,0,0.08)]">

      {/* ── DOCUMENT INFO ── */}
      <div className="px-4 pt-5 pb-4 border-b border-border">
        <SidebarSection title={ts('notes.documentInfo', 'Document Info')} />

        <div className="space-y-3">
          {/* Type */}
          <div className="flex items-center justify-between">
            <p className="text-[11px] font-semibold text-muted-foreground">{ts('notes.type', 'Type')}</p>
            <span
              className={cn(
                "inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium",
                note.type === 'checklist'
                  ? "bg-emerald-50 border-emerald-200 text-emerald-700"
                  : note.isMarkdown
                    ? "bg-violet-50 border-violet-200 text-violet-700"
                    : "bg-card border-slate-200 text-slate-600"
              )}
            >
              {note.type === 'checklist'
                ? <ListChecks className="h-3 w-3" />
                : <FileText className="h-3 w-3" />}
              {noteTypeLabel}
            </span>
          </div>

          {/* Word count — discreet inline row */}
          <div className="flex items-center justify-between">
            <p className="text-[11px] font-semibold text-muted-foreground">{ts('notes.wordCount', 'Mots')}</p>
            <p className="text-[13px] font-semibold text-foreground/70 tabular-nums">
              {wordCount.toLocaleString()} <span className="text-[10px] font-normal text-muted-foreground">{ts('notes.words', 'mots')}</span>
            </p>
          </div>

          {/* Labels */}
          {Array.isArray(note.labels) && note.labels.length > 0 && (
            <div>
              <p className="mb-1.5 text-[11px] font-semibold text-muted-foreground">{ts('notes.labels', 'Labels')}</p>
              <div className="flex flex-wrap gap-1">
                {note.labels.map((label) => (
                  <span
                    key={label}
                    className="inline-flex items-center gap-1 rounded-full border border-slate-200 bg-card px-2 py-0.5 text-[11px] font-medium text-foreground/70"
                  >
                    <Hash className="h-2.5 w-2.5" />
                    {label}
                  </span>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>

      {/* ── ACTIONS ── */}
      <div className="px-4 pt-4 pb-5 flex-1">
        <SidebarSection title={ts('notes.actions', 'Actions')} />

        <div className="space-y-0.5">

          {/* Move to notebook */}
          <Popover open={moveOpen} onOpenChange={setMoveOpen}>
            <PopoverTrigger asChild>
              <button
                type="button"
                className="group flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-[13px] font-medium text-foreground/70 hover:bg-stone-100/80 hover:text-stone-800 dark:hover:bg-white/5 dark:hover:text-stone-100 transition-all duration-150"
              >
                <span className="text-muted-foreground group-hover:text-stone-600 dark:group-hover:text-stone-300 transition-colors">
                  {isMoving
                    ? <Loader2 className="h-3.5 w-3.5 animate-spin" />
                    : <FolderInput className="h-3.5 w-3.5" />}
                </span>
                {t('notebookSuggestion.moveToNotebook')}
              </button>
            </PopoverTrigger>
            <PopoverContent side="left" align="start" className="w-52 p-1.5">
              <div className="mb-1 px-2 py-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60">
                {t('notebookSuggestion.moveToNotebook')}
              </div>
              <button
                type="button"
                onClick={() => handleMoveToNotebook(null)}
                className={cn(
                  'flex w-full items-center gap-2 rounded px-2 py-1.5 text-[12px] font-medium hover:bg-muted transition-colors',
                  !note.notebookId ? 'text-primary' : 'text-foreground/70'
                )}
              >
                {!note.notebookId
                  ? <Check className="h-3 w-3 shrink-0" />
                  : <span className="h-3 w-3 shrink-0" />}
                {t('notes.generalNotes')}
              </button>
              {notebooks.map((nb) => (
                <button
                  key={nb.id}
                  type="button"
                  onClick={() => handleMoveToNotebook(nb.id)}
                  className={cn(
                    'flex w-full items-center gap-2 rounded px-2 py-1.5 text-[12px] font-medium hover:bg-muted transition-colors',
                    note.notebookId === nb.id ? 'text-primary' : 'text-foreground/70'
                  )}
                >
                  {note.notebookId === nb.id
                    ? <Check className="h-3 w-3 shrink-0" />
                    : <span className="h-3 w-3 shrink-0" />}
                  {nb.name}
                </button>
              ))}
            </PopoverContent>
          </Popover>

          {/* Pin / Unpin */}
          <SidebarActionBtn
            icon={note.isPinned ? <PinOff className="h-3.5 w-3.5" /> : <Pin className="h-3.5 w-3.5" />}
            label={note.isPinned ? t('notes.unpin') : t('notes.pin')}
            onClick={() => onPinToggle(note)}
          />

          {/* Archive */}
          <SidebarActionBtn
            icon={<Archive className="h-3.5 w-3.5" />}
            label={t('notes.archive')}
            onClick={() => onArchive(note)}
          />

          {/* Reminder */}
          {onUpdateReminder && (
            <>
              <SidebarActionBtn
                icon={<Bell className={cn("h-3.5 w-3.5", note.reminder && "text-primary")} />}
                label={t('reminder.setReminder')}
                onClick={() => setShowReminder(true)}
              />
              <div onClick={(e) => e.stopPropagation()}>
                <ReminderDialog
                  open={showReminder}
                  onOpenChange={setShowReminder}
                  currentReminder={note.reminder ? new Date(note.reminder) : null}
                  onSave={(date) => {
                    onUpdateReminder(note.id, date)
                    setShowReminder(false)
                  }}
                  onRemove={() => {
                    onUpdateReminder(note.id, null)
                    setShowReminder(false)
                  }}
                />
              </div>
            </>
          )}

          {/* History */}
          <SidebarActionBtn
            icon={<History className="h-3.5 w-3.5" />}
            label={
              note.historyEnabled
                ? ts('notes.history', 'Historique')
                : ts('notes.enableHistory', "Activer l'historique")
            }
            onClick={() => void handleHistory()}
          />

        </div>
      </div>
    </aside>
  )
}

// ─── Main Component ───────────────────────────────────────────────────────────

export function NotesTabsView({
  notes,
  onEdit,
  currentNotebookId,

  noteHistoryMode = 'manual',
  onOpenHistory,
  onEnableHistory,
  onNoteCreated,
}: NotesTabsViewProps) {
  const { t, language } = useLanguage()
  const { triggerRefresh } = useNoteRefreshOptional()
  const [items, setItems] = useState<Note[]>(notes)
  const [selectedId, setSelectedId] = useState<string | null>(null)
  const [isCreating, startCreating] = useTransition()
  const [noteToDelete, setNoteToDelete] = useState<Note | null>(null)
  const [sortOrder, setSortOrder] = useState<SortOrder>('date-desc')
  const [sidebarOpen, setSidebarOpen] = useState(true)
  const [listOpen, setListOpen] = useState(true)

  // Auto-hide meta sidebar when AI opens; restore previous state when AI closes
  const prevSidebarOpen = useRef(true)
  useEffect(() => {
    const handler = (e: Event) => {
      const visible = (e as CustomEvent<boolean>).detail
      if (visible) {
        prevSidebarOpen.current = sidebarOpen
        setSidebarOpen(false)
      } else {
        setSidebarOpen(prevSidebarOpen.current)
      }
    }
    window.addEventListener('contextual-ai-visibility', handler)
    return () => window.removeEventListener('contextual-ai-visibility', handler)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [sidebarOpen])

  const prevNotesRef = useRef<Note[]>(notes)

  useEffect(() => {
    setItems((prev) => {
      const prevIds = prev.map((n) => n.id).join(',')
      const incomingIds = notes.map((n) => n.id).join(',')

      const merge = (fresh: Note, local: Note) => {
        // Detect if the server explicitly changed content since last sync
        const prevServer = prevNotesRef.current.find((n) => n.id === fresh.id)
        const serverContentChanged = prevServer ? prevServer.content !== fresh.content : false
        const serverTitleChanged = prevServer ? prevServer.title !== fresh.title : false
        const serverCheckItemsChanged = prevServer
          ? JSON.stringify(prevServer.checkItems) !== JSON.stringify(fresh.checkItems)
          : false
        const labelsChanged =
          JSON.stringify(fresh.labels?.slice().sort()) !==
          JSON.stringify(local.labels?.slice().sort())

        return {
          ...fresh,
          title: serverTitleChanged ? fresh.title : local.title || fresh.title,
          content: serverContentChanged ? fresh.content : local.content,
          checkItems: serverCheckItemsChanged ? fresh.checkItems : local.checkItems,
          labels: labelsChanged ? fresh.labels : local.labels,
        }
      }

      let result: Note[]
      if (prevIds === incomingIds) {
        result = prev.map((local) => {
          const fresh = notes.find((n) => n.id === local.id)
          return fresh ? merge(fresh, local) : local
        })
      } else {
        result = notes.map((fresh) => {
          const local = prev.find((p) => p.id === fresh.id)
          return local ? merge(fresh, local) : fresh
        })
      }

      return result
    })

    prevNotesRef.current = notes
  }, [notes])

  useEffect(() => {
    if (items.length === 0) {
      setSelectedId(null)
      return
    }
    setSelectedId((prev) =>
      prev && items.some((n) => n.id === prev) ? prev : items[0].id
    )
  }, [items])

  useEffect(() => {
    const handler = (e: Event) => {
      const { name } = (e as CustomEvent).detail
      if (!name) return
      setItems((prev) =>
        prev.map((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 }
        })
      )
    }
    window.addEventListener('label-deleted', handler)
    return () => window.removeEventListener('label-deleted', handler)
  }, [])

  // Sorted display items — pinned notes always float to the top
  const sortedItems = useMemo(() => {
    const pinned = items.filter(n => n.isPinned)
    const unpinned = items.filter(n => !n.isPinned)
    const sortFn = (a: Note, b: Note) => {
      if (sortOrder === 'date-desc') return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
      if (sortOrder === 'date-asc') return new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime()
      if (sortOrder === 'title-asc') return (a.title || '').localeCompare(b.title || '')
      if (sortOrder === 'title-desc') return (b.title || '').localeCompare(a.title || '')
      return 0
    }
    return [...pinned.sort(sortFn), ...unpinned.sort(sortFn)]
  }, [items, sortOrder])

  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
  )

  const handleDragEnd = useCallback(
    async (event: DragEndEvent) => {
      const { active, over } = event
      if (!over || active.id === over.id) return
      const oldIndex = items.findIndex((n) => n.id === active.id)
      const newIndex = items.findIndex((n) => n.id === over.id)
      if (oldIndex < 0 || newIndex < 0) return
      const reordered = arrayMove(items, oldIndex, newIndex)
      setItems(reordered)
      try {
        await updateFullOrderWithoutRevalidation(reordered.map((n) => n.id))
      } catch {
        setItems(notes)
        toast.error(t('notes.moveFailed'))
      }
    },
    [items, notes, t]
  )

  const selected = items.find((n) => n.id === selectedId) ?? null
  const colorKey = selected ? getColorKey(selected) : 'default'


  const handleCreateNote = (noteType: NoteType = 'richtext') => {
    startCreating(async () => {
      try {
        const newNote = await createNote({
          content: '',
          type: noteType,
          checkItems: noteType === 'checklist' ? [{ id: Date.now().toString(), text: '', checked: false }] : undefined,
          title: undefined,
          notebookId: currentNotebookId || undefined,
          skipRevalidation: true
        })
        if (!newNote) return
        setItems((prev) => {
          const pinned = prev.filter(n => n.isPinned)
          const unpinned = prev.filter(n => !n.isPinned)
          return [...pinned, newNote, ...unpinned]
        })
        setSelectedId(newNote.id)
        onNoteCreated?.(newNote)
        // NOTE: No triggerRefresh() here — the note is already added to items above.
        // triggerRefresh() would call getAllNotes() which may return stale cache
        // in production (skipRevalidation:true skips cache invalidation).
      } catch {
        toast.error(t('notes.createFailed') || 'Impossible de créer la note')
      }
    })
  }

  const handlePinToggle = async (note: Note) => {
    const next = !note.isPinned
    setItems((prev) => prev.map((n) => n.id === note.id ? { ...n, isPinned: next } : n))
    try {
      await updateNote(note.id, { isPinned: next }, { skipRevalidation: true })
      triggerRefresh()
      toast.success(next ? (t('notes.pinned') || 'Épinglée') : (t('notes.unpinned') || 'Désépinglée'))
    } catch {
      setItems((prev) => prev.map((n) => n.id === note.id ? { ...n, isPinned: note.isPinned } : n))
      toast.error(t('notes.updateFailed') || 'Mise à jour échouée')
    }
  }

  const handleArchive = async (note: Note) => {
    try {
      await toggleArchive(note.id, true)
      setItems((prev) => prev.filter((n) => n.id !== note.id))
      setSelectedId((prev) => (prev === note.id ? null : prev))
      triggerRefresh()
      toast.success(t('notes.archived') || 'Note archivée')
    } catch {
      toast.error(t('notes.archiveFailed') || 'Archivage échoué')
    }
  }

  return (
    <div
      className="flex min-h-0 flex-1 gap-0 overflow-hidden rounded-xl border border-border/70 shadow-sm"
      style={{ height: 'max(360px, min(85vh, calc(100vh - 9rem)))' }}
      data-testid="notes-grid-tabs"
    >
      {/* ── Left panel: note list ── */}
      <div className={cn('flex shrink-0 flex-col border-r border-border/60 bg-background transition-all duration-200', listOpen ? 'w-64' : 'w-9')}>

        {/* Header — always visible */}
        <div className={cn('flex items-center border-b border-border/60 bg-background/95 py-3.5 transition-all', listOpen ? 'justify-between px-4' : 'justify-center px-1')}>
          {listOpen && (
            <div className="flex items-center gap-2 min-w-0">
              <span className="text-sm font-semibold tracking-tight text-foreground">
                {t('notes.title')}
              </span>
              <span className="rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-semibold text-primary">
                {items.length}
              </span>
            </div>
          )}
          <div className={cn('flex items-center', listOpen ? 'gap-1' : 'flex-col gap-1')}>
            {/* Sort / filter button */}
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button
                  variant="ghost"
                  size="sm"
                  className={cn(
                    'h-7 w-7 p-0 text-muted-foreground/70 hover:bg-primary/8 hover:text-primary',
                    sortOrder !== 'date-desc' && 'text-primary bg-primary/8'
                  )}
                  title={t('notes.sort') || 'Trier'}
                >
                  <ListFilter className="h-3.5 w-3.5" />
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end" className="w-44">
                <DropdownMenuLabel className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60 py-1">
                  {t('notes.sortBy') || 'Trier par'}
                </DropdownMenuLabel>
                <DropdownMenuSeparator />
                <DropdownMenuRadioGroup value={sortOrder} onValueChange={(v) => setSortOrder(v as SortOrder)}>
                  <DropdownMenuRadioItem value="date-desc" className="text-[13px]">
                    {t('notes.sortDateDesc') || 'Date (récent)'}
                  </DropdownMenuRadioItem>
                  <DropdownMenuRadioItem value="date-asc" className="text-[13px]">
                    {t('notes.sortDateAsc') || 'Date (ancien)'}
                  </DropdownMenuRadioItem>
                  <DropdownMenuRadioItem value="title-asc" className="text-[13px]">
                    {t('notes.sortTitleAsc') || 'Titre A → Z'}
                  </DropdownMenuRadioItem>
                  <DropdownMenuRadioItem value="title-desc" className="text-[13px]">
                    {t('notes.sortTitleDesc') || 'Titre Z → A'}
                  </DropdownMenuRadioItem>
                </DropdownMenuRadioGroup>
              </DropdownMenuContent>
            </DropdownMenu>

            {/* New note button — dropdown to choose type */}
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button
                  variant="ghost"
                  size="sm"
                  className="h-7 w-7 p-0 text-muted-foreground/70 hover:bg-primary/8 hover:text-primary"
                  disabled={isCreating}
                  title={t('notes.newNote')}
                >
                  {isCreating
                    ? <Loader2 className="h-3.5 w-3.5 animate-spin" />
                    : <Plus className="h-4 w-4" />}
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end" className="min-w-[160px]">
                <DropdownMenuItem onClick={() => handleCreateNote('richtext')}>
                  <FileText className="h-4 w-4 mr-2" />
                  {t('notes.newNote') || 'Note'}
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => handleCreateNote('checklist')}>
                  <ListChecks className="h-4 w-4 mr-2" />
                  {t('notes.newChecklist') || 'Checklist'}
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
            {/* Collapse list panel */}
            {listOpen && (
              <Button
                variant="ghost"
                size="sm"
                className="h-7 w-7 p-0 text-muted-foreground/70 hover:bg-primary/8 hover:text-primary"
                onClick={() => setListOpen(false)}
                title="R\u00e9duire la liste"
              >
                <PanelLeftClose className="h-3.5 w-3.5" />
              </Button>
            )}
          </div>
        </div>

        {/* Scrollable note list */}
        {listOpen && (
        <div
          className="flex-1 overflow-y-auto overscroll-contain bg-background"
          role="listbox"
          aria-label={t('notes.viewTabs')}
        >
          {items.length === 0 ? (
            <div className="flex flex-col items-center justify-center px-6 py-16 text-center">
              <div className="mb-4 rounded-2xl border border-border/60 bg-muted/30 p-4">
                <FileText className="h-6 w-6 text-muted-foreground/40" />
              </div>
              <p className="text-sm font-medium text-muted-foreground">{t('notes.emptyStateTabs') || 'Aucune note'}</p>
              <p className="mt-1 text-xs text-muted-foreground/60">{t('notes.createFirst') || 'Créez votre première note'}</p>
            </div>
          ) : (
            <DndContext
              id="notes-tabs-dnd"
              sensors={sensors}
              collisionDetection={closestCenter}
              onDragEnd={handleDragEnd}
            >
              <SortableContext
                items={items.map((n) => n.id)}
                strategy={verticalListSortingStrategy}
              >
                <div className="flex flex-col">
                  {sortedItems.map((note) => (
                    <SortableNoteListItem
                      key={note.id}
                      note={note}
                      selected={note.id === selectedId}
                      onSelect={() => setSelectedId(note.id)}
                      onDelete={() => setNoteToDelete(note)}
                      reorderLabel={t('notes.reorderTabs')}
                      deleteLabel={t('notes.delete')}
                      language={language}
                      untitledLabel={t('notes.untitled')}
                    />
                  ))}
                </div>
              </SortableContext>
            </DndContext>
          )}
        </div>
        )}
        {/* Expand button shown in collapsed state */}
        {!listOpen && (
          <div className="flex flex-col items-center pt-3">
            <Button
              variant="ghost"
              size="sm"
              className="h-7 w-7 p-0 text-muted-foreground/70 hover:bg-primary/8 hover:text-primary"
              onClick={() => setListOpen(true)}
              title="Afficher la liste"
            >
              <PanelLeftOpen className="h-3.5 w-3.5" />
            </Button>
          </div>
        )}
      </div>

      {/* ── Right content panel ── */}
      {selected ? (
        <div className="flex min-w-0 flex-1 overflow-hidden">
          {/* Editor */}
          <div
            className={cn(
              'relative flex min-w-0 flex-1 flex-col overflow-hidden bg-gradient-to-b',
              COLOR_PANEL_BG[colorKey]
            )}
          >
            <NoteInlineEditor
              key={`${selected.id}-${String(selected.updatedAt)}`}
              note={selected}
              noteHistoryMode={noteHistoryMode}
              onOpenHistory={onOpenHistory}
              onEnableHistory={onEnableHistory}
              colorKey={colorKey}
              defaultPreviewMode={true}
              onChange={(noteId, fields) => {
                setItems((prev) =>
                  prev.map((n) => (n.id === noteId ? { ...n, ...fields } : n))
                )
              }}
              onDelete={(noteId) => {
                setItems((prev) => prev.filter((n) => n.id !== noteId))
                setSelectedId((prev) => (prev === noteId ? null : prev))
              }}
              onArchive={(noteId) => {
                setItems((prev) => prev.filter((n) => n.id !== noteId))
                setSelectedId((prev) => (prev === noteId ? null : prev))
                triggerRefresh()
              }}
            />
            {/* Toggle sidebar button — top-right of editor, always visible */}
            <button
              type="button"
              onClick={() => setSidebarOpen((v) => !v)}
              title={sidebarOpen ? 'Masquer le panneau' : 'Afficher le panneau'}
              className="absolute top-3 right-3 z-20 flex h-7 w-7 items-center justify-center rounded-md border border-border/70 bg-background/90 backdrop-blur-sm shadow-sm text-muted-foreground hover:text-primary hover:border-primary/40 hover:bg-primary/5 transition-colors"
            >
              {sidebarOpen
                ? <PanelRightClose className="h-3.5 w-3.5" />
                : <PanelRightOpen className="h-3.5 w-3.5" />}
            </button>
          </div>

          {/* Meta sidebar — collapsible */}
          {sidebarOpen && (
            <NoteMetaSidebar
              note={selected}
              onPinToggle={handlePinToggle}
              onArchive={handleArchive}
              onOpenHistory={onOpenHistory}
              onEnableHistory={onEnableHistory}
              onUpdateReminder={async (noteId, reminder) => {
                try {
                  await updateNote(noteId, { reminder })
                  setItems((prev) =>
                    prev.map((n) => (n.id === noteId ? { ...n, reminder } : n))
                  )
                  if (reminder) {
                    toast.success(t('notes.reminderSet', { datetime: reminder.toLocaleString() }))
                  } else {
                    toast.info(t('reminder.removeReminder'))
                  }
                  triggerRefresh()
                } catch {
                  toast.error(t('general.error'))
                }
              }}
            />
          )}
        </div>
      ) : (
        <div className="flex min-w-0 flex-1 items-center justify-center bg-muted/10">
          <div className="px-10 text-center">
            <div className="mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-2xl border border-border/60 bg-background shadow-sm">
              <FileText className="h-7 w-7 text-muted-foreground/30" />
            </div>
            <p className="text-sm font-medium text-foreground/60">
              {items.length === 0 ? t('notes.emptyNotebook') : t('notes.noNoteSelected')}
            </p>
            <p className="mt-1.5 text-xs text-muted-foreground/50 max-w-[200px] mx-auto leading-relaxed">
              {items.length === 0
                ? t('notes.emptyNotebookDesc')
                : t('notes.selectOrCreateNote')}
            </p>
          </div>
        </div>
      )}

      {/* Delete Confirmation Dialog */}
      <Dialog open={!!noteToDelete} onOpenChange={() => setNoteToDelete(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>{t('notes.confirmDeleteTitle') || t('notes.delete')}</DialogTitle>
            <DialogDescription>
              {t('notes.confirmDelete') || 'Are you sure you want to delete this note?'}
              {noteToDelete && (
                <span className="mt-2 block font-medium text-foreground">
                  &quot;{getNoteDisplayTitle(noteToDelete, t('notes.untitled'))}&quot;
                </span>
              )}
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={() => setNoteToDelete(null)}>
              {t('common.cancel')}
            </Button>
            <Button
              variant="destructive"
              onClick={async () => {
                if (!noteToDelete) return
                try {
                  await deleteNote(noteToDelete.id, { skipRevalidation: true })
                  setItems((prev) => prev.filter((n) => n.id !== noteToDelete.id))
                  setSelectedId((prev) => (prev === noteToDelete.id ? null : prev))
                  setNoteToDelete(null)
                  triggerRefresh()
                  toast.success(t('notes.deleted'))
                } catch {
                  toast.error(t('notes.deleteFailed'))
                }
              }}
            >
              {t('notes.delete')}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  )
}
