'use client'

import { useState, useEffect, useRef, useCallback, useTransition } from 'react'
import { Note, CheckItem, NOTE_COLORS, NoteColor } from '@/lib/types'
import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { LabelBadge } from '@/components/label-badge'
import { EditorConnectionsSection } from '@/components/editor-connections-section'
import { FusionModal } from '@/components/fusion-modal'
import { ComparisonModal } from '@/components/comparison-modal'
import { useLanguage } from '@/lib/i18n'
import { cn } from '@/lib/utils'
import {
  updateNote,
  deleteNote,
  createNote,
} from '@/app/actions/notes'
import { fetchLinkMetadata } from '@/app/actions/scrape'
import {
  Pin,
  Palette,
  Archive,
  ArchiveRestore,
  Trash2,
  ImageIcon,
  Link as LinkIcon,
  X,
  Plus,
  CheckSquare,
  FileText,
  Eye,
  Sparkles,
  Loader2,
  Check,
  RotateCcw,
} from 'lucide-react'
import { toast } from 'sonner'
import { MarkdownContent } from '@/components/markdown-content'
import { EditorImages } from '@/components/editor-images'
import { useAutoTagging } from '@/hooks/use-auto-tagging'
import { GhostTags } from '@/components/ghost-tags'
import { useTitleSuggestions } from '@/hooks/use-title-suggestions'
import { TitleSuggestions } from '@/components/title-suggestions'
import { useLabels } from '@/context/LabelContext'
import { useNoteRefresh } from '@/context/NoteRefreshContext'
import { useNotebooks } from '@/context/notebooks-context'
import { ContextualAIChat } from '@/components/contextual-ai-chat'
import { formatDistanceToNow } from 'date-fns'
import { fr } from 'date-fns/locale/fr'
import { enUS } from 'date-fns/locale/en-US'
import { useSession } from 'next-auth/react'
import { getAISettings } from '@/app/actions/ai-settings'

interface NoteInlineEditorProps {
  note: Note
  onDelete?: (noteId: string) => void
  onArchive?: (noteId: string) => void
  onChange?: (noteId: string, fields: Partial<Note>) => void
  colorKey: NoteColor
  /** If true and the note is a Markdown note, open directly in preview mode */
  defaultPreviewMode?: boolean
}

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

/** Save content via REST API (not Server Action) to avoid Next.js implicit router re-renders */
async function saveInline(
  id: string,
  data: { title?: string | null; content?: string; checkItems?: CheckItem[]; isMarkdown?: boolean }
) {
  await fetch(`/api/notes/${id}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  })
}

export function NoteInlineEditor({
  note,
  onDelete,
  onArchive,
  onChange,
  colorKey,
  defaultPreviewMode = false,
}: NoteInlineEditorProps) {
  const { t, language } = useLanguage()
  const { data: session } = useSession()
  const [aiAssistantEnabled, setAiAssistantEnabled] = useState(true)
  const [autoLabelingEnabled, setAutoLabelingEnabled] = useState(true)

  useEffect(() => {
    if (session?.user?.id) {
      const userId = session.user.id
      import('@/app/actions/ai-settings').then(({ getAISettings }) => {
        getAISettings(userId).then(settings => {
          setAiAssistantEnabled(settings.paragraphRefactor !== false)
          setAutoLabelingEnabled(settings.autoLabeling !== false)
        }).catch(err => console.error("Failed to fetch AI settings", err))
      })
    }
  }, [session?.user?.id])
  const { labels: globalLabels, addLabel } = useLabels()
  const [, startTransition] = useTransition()
  const { triggerRefresh } = useNoteRefresh()

  // ── Local edit state ──────────────────────────────────────────────────────
  const [title, setTitle] = useState(note.title || '')
  const [content, setContent] = useState(note.content || '')
  const [checkItems, setCheckItems] = useState<CheckItem[]>(note.checkItems || [])
  const [isMarkdown, setIsMarkdown] = useState(note.isMarkdown || false)
  const [showMarkdownPreview, setShowMarkdownPreview] = useState(
    defaultPreviewMode && (note.isMarkdown || false)
  )
  const [isDirty, setIsDirty] = useState(false)
  const [isSaving, setIsSaving] = useState(false)
  const [dismissedTags, setDismissedTags] = useState<string[]>([])
  const [fusionNotes, setFusionNotes] = useState<Array<Partial<Note>>>([])
  const [comparisonNotes, setComparisonNotes] = useState<Array<Partial<Note>>>([])

  const changeTitle = (t: string) => { setTitle(t); onChange?.(note.id, { title: t }) }
  const changeContent = (c: string) => { setContent(c); onChange?.(note.id, { content: c }) }
  const changeCheckItems = (ci: CheckItem[]) => { setCheckItems(ci); onChange?.(note.id, { checkItems: ci }) }

  // Link dialog
  const [linkUrl, setLinkUrl] = useState('')
  const [showLinkInput, setShowLinkInput] = useState(false)
  const [isAddingLink, setIsAddingLink] = useState(false)

  // AI side panel
  const [aiOpen, setAiOpen] = useState(false)
  const [isProcessingAI, setIsProcessingAI] = useState(false)
  // Undo after AI copilot applies content
  const [previousContent, setPreviousContent] = useState<string | null>(null)

  // Notebooks list (for copilot chat scope)
  const { notebooks } = useNotebooks()

  const fileInputRef = useRef<HTMLInputElement>(null)
  const saveTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
  const pendingRef = useRef({ title, content, checkItems, isMarkdown })
  const noteIdRef = useRef(note.id)

  // Title suggestions
  const [dismissedTitleSuggestions, setDismissedTitleSuggestions] = useState(false)
  const { suggestions: titleSuggestions, isAnalyzing: isAnalyzingTitles } = useTitleSuggestions({
    content: note.type === 'text' ? content : '',
    enabled: note.type === 'text' && !title
  })

  // Keep pending ref in sync for unmount save
  useEffect(() => {
    pendingRef.current = { title, content, checkItems, isMarkdown }
  }, [title, content, checkItems, isMarkdown])

  // ── Sync when selected note switches ─────────────────────────────────────
  useEffect(() => {
    // Flush unsaved changes for the PREVIOUS note before switching
    if (isDirty && noteIdRef.current !== note.id) {
      const { title: t, content: c, checkItems: ci, isMarkdown: im } = pendingRef.current
      saveInline(noteIdRef.current, {
        title: t.trim() || null,
        content: c,
        checkItems: note.type === 'checklist' ? ci : undefined,
        isMarkdown: im,
      }).catch(() => {})
    }

    noteIdRef.current = note.id
    setTitle(note.title || '')
    setContent(note.content || '')
    setCheckItems(note.checkItems || [])
    setIsMarkdown(note.isMarkdown || false)
    setShowMarkdownPreview(defaultPreviewMode && (note.isMarkdown || false))
    setIsDirty(false)
    setDismissedTitleSuggestions(false)
    clearTimeout(saveTimerRef.current)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [note.id])

  // ── Auto-save (1.5 s debounce, skipContentTimestamp) ─────────────────────
  const scheduleSave = useCallback(() => {
    setIsDirty(true)
    clearTimeout(saveTimerRef.current)
    saveTimerRef.current = setTimeout(async () => {
      const { title: t, content: c, checkItems: ci, isMarkdown: im } = pendingRef.current
      setIsSaving(true)
      try {
        await saveInline(noteIdRef.current, {
          title: t.trim() || null,
          content: c,
          checkItems: note.type === 'checklist' ? ci : undefined,
          isMarkdown: im,
        })
        setIsDirty(false)
      } catch {
        // silent — retry on next keystroke
      } finally {
        setIsSaving(false)
      }
    }, 1500)
  }, [note.type])

  // Flush on unmount
  useEffect(() => {
    return () => {
      clearTimeout(saveTimerRef.current)
      const { title: t, content: c, checkItems: ci, isMarkdown: im } = pendingRef.current
      saveInline(noteIdRef.current, {
        title: t.trim() || null,
        content: c,
        checkItems: note.type === 'checklist' ? ci : undefined,
        isMarkdown: im,
      }).catch(() => {})
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // ── Auto-tagging ──────────────────────────────────────────────────────────
  const { suggestions, isAnalyzing } = useAutoTagging({
    content: note.type === 'text' ? content : '',
    notebookId: note.notebookId,
    enabled: note.type === 'text' && autoLabelingEnabled,
  })
  const existingLabelsLower = (note.labels || []).map((l) => l.toLowerCase())
  const filteredSuggestions = suggestions.filter(
    (s) => s?.tag && !dismissedTags.includes(s.tag) && !existingLabelsLower.includes(s.tag.toLowerCase())
  )
  const handleSelectGhostTag = async (tag: string) => {
    const exists = (note.labels || []).some((l) => l.toLowerCase() === tag.toLowerCase())
    if (!exists) {
      const newLabels = [...(note.labels || []), tag]
      // Optimistic UI — update sidebar immediately, no page refresh needed
      onChange?.(note.id, { labels: newLabels })
      await updateNote(note.id, { labels: newLabels }, { skipRevalidation: true })
      const globalExists = globalLabels.some((l) => l.name.toLowerCase() === tag.toLowerCase())
      if (!globalExists) {
        try { await addLabel(tag) } catch {}
      }
      toast.success(t('ai.tagAdded', { tag }))
    }
  }

  const fetchNotesByIds = async (noteIds: string[]) => {
    const fetched = await Promise.all(noteIds.map(async (id) => {
      try {
        const res = await fetch(`/api/notes/${id}`)
        if (!res.ok) return null
        const data = await res.json()
        return data.success && data.data ? data.data : null
      } catch { return null }
    }))
    return fetched.filter((n: any) => n !== null) as Array<Partial<Note>>
  }

  const handleMergeNotes = async (noteIds: string[]) => {
    setFusionNotes(await fetchNotesByIds(noteIds))
  }

  const handleCompareNotes = async (noteIds: string[]) => {
    setComparisonNotes(await fetchNotesByIds(noteIds))
  }

  const handleConfirmFusion = async ({ title, content }: { title: string; content: string }, options: { archiveOriginals: boolean; keepAllTags: boolean; useLatestTitle: boolean; createBacklinks: boolean }) => {
    await createNote({
      title,
      content,
      labels: options.keepAllTags
        ? [...new Set(fusionNotes.flatMap(n => n.labels || []))]
        : fusionNotes[0].labels || [],
      color: fusionNotes[0].color,
      type: 'text',
      isMarkdown: true,
      autoGenerated: true,
      aiProvider: 'fusion',
      notebookId: fusionNotes[0].notebookId ?? undefined
    })
    if (options.archiveOriginals) {
      for (const n of fusionNotes) {
        if (n.id) await updateNote(n.id, { isArchived: true })
      }
    }
    toast.success(t('toast.notesFusionSuccess'))
    setFusionNotes([])
    triggerRefresh()
  }

  // ── Quick actions (pin, archive, color, delete) ───────────────────────────
  const handleTogglePin = () => {
    const prev = note.isPinned
    startTransition(async () => {
      onChange?.(note.id, { isPinned: !prev })
      try {
        await updateNote(note.id, { isPinned: !prev }, { skipRevalidation: true })
        toast.success(prev ? t('notes.unpinned')  : t('notes.pinned') )
      } catch {
        onChange?.(note.id, { isPinned: prev })
        toast.error(t('general.error'))
      }
    })
  }

  const handleToggleArchive = () => {
    startTransition(async () => {
      onArchive?.(note.id)
      try {
        await updateNote(note.id, { isArchived: !note.isArchived }, { skipRevalidation: true })
      } catch {
        // Cannot easily revert since onArchive removes from list
        toast.error(t('general.error'))
      }
    })
  }

  const handleColorChange = (color: string) => {
    const prev = color
    startTransition(async () => {
      onChange?.(note.id, { color })
      try {
        await updateNote(note.id, { color }, { skipRevalidation: true })
      } catch {
        onChange?.(note.id, { color: prev })
        toast.error(t('general.error'))
      }
    })
  }

  const handleDelete = () => {
    if (!confirm(t('notes.confirmDelete'))) return
    startTransition(async () => {
      await deleteNote(note.id)
      onDelete?.(note.id)
      triggerRefresh()
    })
  }

  // ── Image upload ──────────────────────────────────────────────────────────
  const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files
    if (!files) return
    for (const file of Array.from(files)) {
      const formData = new FormData()
      formData.append('file', file)
      try {
        const res = await fetch('/api/upload', { method: 'POST', body: formData })
        if (!res.ok) throw new Error('Upload failed')
        const data = await res.json()
        const newImages = [...(note.images || []), data.url]
        onChange?.(note.id, { images: newImages })
        await updateNote(note.id, { images: newImages })
      } catch {
        toast.error(t('notes.uploadFailed', { filename: file.name }))
      }
    }
    if (fileInputRef.current) fileInputRef.current.value = ''
  }

  const handleRemoveImage = async (index: number) => {
    const newImages = (note.images || []).filter((_, i) => i !== index)
    onChange?.(note.id, { images: newImages })
    await updateNote(note.id, { images: newImages })
  }

  // ── Link ──────────────────────────────────────────────────────────────────
  const handleAddLink = async () => {
    if (!linkUrl) return
    setIsAddingLink(true)
    try {
      const metadata = await fetchLinkMetadata(linkUrl)
      const newLink = metadata || { url: linkUrl, title: linkUrl }
      const newLinks = [...(note.links || []), newLink]
      onChange?.(note.id, { links: newLinks })
      await updateNote(note.id, { links: newLinks })
      toast.success(t('notes.linkAdded'))
    } catch {
      toast.error(t('notes.linkAddFailed'))
    } finally {
      setLinkUrl('')
      setShowLinkInput(false)
      setIsAddingLink(false)
    }
  }

  const handleRemoveLink = async (index: number) => {
    const newLinks = (note.links || []).filter((_, i) => i !== index)
    onChange?.(note.id, { links: newLinks })
    await updateNote(note.id, { links: newLinks })
  }

  // ── Checklist helpers ─────────────────────────────────────────────────────
  const handleToggleCheckItem = (id: string) => {
    const updated = checkItems.map((ci) =>
      ci.id === id ? { ...ci, checked: !ci.checked } : ci
    )
    setCheckItems(updated)
    scheduleSave()
  }

  const handleUpdateCheckText = (id: string, text: string) => {
    const updated = checkItems.map((ci) => (ci.id === id ? { ...ci, text } : ci))
    setCheckItems(updated)
    scheduleSave()
  }

  const handleAddCheckItem = () => {
    const updated = [...checkItems, { id: Date.now().toString(), text: '', checked: false }]
    setCheckItems(updated)
    scheduleSave()
  }

  const handleRemoveCheckItem = (id: string) => {
    const updated = checkItems.filter((ci) => ci.id !== id)
    setCheckItems(updated)
    scheduleSave()
  }

  const dateLocale = getDateLocale(language)

  return (
    <div className="flex h-full w-full overflow-hidden">
      <div className="flex flex-1 min-w-0 flex-col overflow-hidden transition-all duration-300">

      {/* ── Toolbar ───────────────────────────────────────────────── */}
      <div className="flex shrink-0 items-center justify-between border-b border-border/30 px-4 py-1.5 gap-2">

        {/* Left group: content tools */}
        <div className="flex items-center gap-0.5">
          <Button variant="ghost" size="icon" className="h-8 w-8"
            title={t('notes.addImage') }
            onClick={() => fileInputRef.current?.click()}>
            <ImageIcon className="h-4 w-4" />
          </Button>
          <input ref={fileInputRef} type="file" accept="image/*" multiple className="hidden" onChange={handleImageUpload} />

          <Button variant="ghost" size="icon" className="h-8 w-8"
            title={t('notes.addLink') }
            onClick={() => setShowLinkInput(!showLinkInput)}>
            <LinkIcon className="h-4 w-4" />
          </Button>

          <Button variant="ghost" size="icon"
            className={cn('h-8 w-8', isMarkdown && 'text-primary bg-primary/10')}
            onClick={() => { setIsMarkdown(!isMarkdown); if (isMarkdown) setShowMarkdownPreview(false); scheduleSave() }}
            title="Markdown">
            <FileText className="h-4 w-4" />
          </Button>

          {isMarkdown && (
            <Button variant="ghost" size="icon" className="h-8 w-8"
              onClick={() => setShowMarkdownPreview(!showMarkdownPreview)}
              title={showMarkdownPreview ? (t('notes.edit')) : (t('notes.preview'))}>
              <Eye className="h-4 w-4" />
            </Button>
          )}

          {note.type === 'text' && aiAssistantEnabled && (
            <Button variant="ghost" size="sm"
              className={cn('h-8 gap-1.5 px-2 text-xs font-medium transition-colors', aiOpen && 'bg-primary/10 text-primary')}
              onClick={() => setAiOpen(!aiOpen)}
              title={t('ai.aiCopilot')}>
              {isProcessingAI
                ? <Loader2 className="h-3.5 w-3.5 animate-spin" />
                : <Sparkles className="h-3.5 w-3.5" />}
              <span className="hidden sm:inline">{t('ai.aiCopilot')}</span>
            </Button>
          )}

          {previousContent !== null && (
            <Button variant="ghost" size="icon" className="h-8 w-8 text-amber-500 hover:text-amber-600"
              title={t('ai.undoAI') }
              onClick={() => { changeContent(previousContent); setPreviousContent(null); scheduleSave(); toast.info(t('ai.undoApplied') ) }}>
              <RotateCcw className="h-3.5 w-3.5" />
            </Button>
          )}
        </div>

        {/* Right group: meta actions + save indicator */}
        <div className="flex items-center gap-1">
          <span className="mr-1 flex items-center gap-1 text-[11px] text-muted-foreground/50 select-none">
            {isSaving ? (
              <><Loader2 className="h-3 w-3 animate-spin" /> {t('notes.saving')}</>
            ) : isDirty ? (
              <><span className="h-1.5 w-1.5 rounded-full bg-amber-400" /> {t('notes.dirtyStatus')}</>
            ) : (
              <><Check className="h-3 w-3 text-emerald-500" /> {t('notes.savedStatus')}</>
            )}
          </span>



          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="ghost" size="icon" className="h-8 w-8" title={t('notes.changeColor')}>
                <Palette className="h-4 w-4" />
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <div className="grid grid-cols-5 gap-2 p-2">
                {Object.entries(NOTE_COLORS).map(([name, cls]) => (
                  <button type="button" key={name}
                    className={cn('h-7 w-7 rounded-full border-2 transition-transform hover:scale-110', cls.bg,
                      note.color === name ? 'border-gray-900 dark:border-gray-100' : 'border-gray-300 dark:border-gray-700')}
                    onClick={() => handleColorChange(name)} title={name} />
                ))}
              </div>
            </DropdownMenuContent>
          </DropdownMenu>

          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="ghost" size="icon" className="h-8 w-8" title={t('notes.moreOptions')}>
                <span className="text-base leading-none text-muted-foreground">⋯</span>
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem onClick={handleToggleArchive}>
                {note.isArchived
                  ? <><ArchiveRestore className="h-4 w-4 mr-2" />{t('notes.unarchive')}</>
                  : <><Archive className="h-4 w-4 mr-2" />{t('notes.archive')}</>}
              </DropdownMenuItem>
              <DropdownMenuSeparator />
              <DropdownMenuItem className="text-red-600 dark:text-red-400" onClick={handleDelete}>
                <Trash2 className="h-4 w-4 mr-2" />{t('notes.delete')}
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </div>
      </div>

      {/* ── Link input bar (inline) ───────────────────────────────────────── */}
      {showLinkInput && (
        <div className="flex shrink-0 items-center gap-2 border-b border-border/30 bg-muted/30 px-4 py-2">
          <input
            type="url"
            className="flex-1 rounded-md border border-border/60 bg-background px-3 py-1.5 text-sm outline-none focus:border-primary"
            placeholder="https://..."
            value={linkUrl}
            onChange={(e) => setLinkUrl(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter') handleAddLink() }}
            autoFocus
          />
          <Button size="sm" disabled={!linkUrl || isAddingLink} onClick={handleAddLink}>
            {isAddingLink ? <Loader2 className="h-4 w-4 animate-spin" /> : t('notes.add')}
          </Button>
          <Button variant="ghost" size="sm" className="h-8 w-8 p-0" onClick={() => { setShowLinkInput(false); setLinkUrl('') }}>
            <X className="h-4 w-4" />
          </Button>
        </div>
      )}

      {/* ── Labels strip + AI suggestions — always visible outside scroll area ─ */}
      {((note.labels?.length ?? 0) > 0 || filteredSuggestions.length > 0 || isAnalyzing) && (
        <div className="flex shrink-0 flex-wrap items-center gap-1.5 border-b border-border/20 px-8 py-2">
          {/* Existing labels */}
          {(note.labels ?? []).map((label) => (
            <LabelBadge key={label} label={label} />
          ))}
          {/* AI-suggested tags inline with labels */}
          <GhostTags
            suggestions={filteredSuggestions}
            addedTags={note.labels || []}
            isAnalyzing={isAnalyzing}
            onSelectTag={handleSelectGhostTag}
            onDismissTag={(tag) => setDismissedTags((p) => [...p, tag])}
          />
        </div>
      )}

      {/* ── Scrollable editing area ── */}
      <div className="flex flex-1 flex-col overflow-y-auto px-6 py-5">
        {/* Title */}
        <div className="group relative flex items-start gap-2 shrink-0 mb-1">
          <input
            type="text"
            dir="auto"
            className="flex-1 bg-transparent text-xl font-semibold tracking-tight text-foreground outline-none placeholder:text-muted-foreground/40"
            placeholder={t('notes.titlePlaceholder') || 'Titre…'}
            value={title}
            onChange={(e) => { changeTitle(e.target.value); scheduleSave() }}
          />
          {!title && content.trim().split(/\s+/).filter(Boolean).length >= 5 && (
            <button type="button"
              onClick={async (e) => {
                e.preventDefault()
                setIsProcessingAI(true)
                try {
                  const res = await fetch('/api/ai/suggest-title', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ content }),
                  })
                  if (res.ok) {
                    const data = await res.json()
                    const suggested = data.title || data.suggestedTitle || ''
                    if (suggested) { changeTitle(suggested); scheduleSave() }
                  }
                } catch { } finally { setIsProcessingAI(false) }
              }}
              disabled={isProcessingAI}
              className="mt-1 shrink-0 rounded-md p-1 text-muted-foreground/40 opacity-0 transition-all hover:bg-muted hover:text-primary group-hover:opacity-100"
              title={t('ai.suggestTitle')}
            >
              {isProcessingAI ? <Loader2 className="h-4 w-4 animate-spin" /> : <Sparkles className="h-4 w-4" />}
            </button>
          )}
        </div>

        {/* Title Suggestions Dropdown / Inline list */}
        {!title && !dismissedTitleSuggestions && titleSuggestions.length > 0 && (
          <div className="mt-2 text-sm shrink-0">
            <TitleSuggestions
              suggestions={titleSuggestions}
              onSelect={(selectedTitle) => { changeTitle(selectedTitle); scheduleSave() }}
              onDismiss={() => setDismissedTitleSuggestions(true)}
            />
          </div>
        )}

        {/* Images */}
        {Array.isArray(note.images) && note.images.length > 0 && (
          <div className="mt-4">
            <EditorImages images={note.images} onRemove={handleRemoveImage} />
          </div>
        )}

        {/* Link previews */}
        {Array.isArray(note.links) && note.links.length > 0 && (
          <div className="mt-4 flex flex-col gap-2">
            {note.links.map((link, idx) => (
              <div key={idx} className="group relative flex overflow-hidden rounded-xl border border-border/60 bg-background/60">
                {link.imageUrl && (
                  <div className="h-auto w-24 shrink-0 bg-cover bg-center" style={{ backgroundImage: `url(${link.imageUrl})` }} />
                )}
                <div className="flex min-w-0 flex-col justify-center gap-0.5 p-3">
                  <p className="truncate text-sm font-medium">{link.title || link.url}</p>
                  {link.description && <p className="line-clamp-1 text-xs text-muted-foreground">{link.description}</p>}
                  <a href={link.url} target="_blank" rel="noopener noreferrer" className="text-[11px] text-primary hover:underline">
                    {(() => { try { return new URL(link.url).hostname } catch { return link.url } })()}
                  </a>
                </div>
                <button type="button"
                  className="absolute right-2 top-2 rounded-full bg-background/80 p-1 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-destructive/10"
                  onClick={() => handleRemoveLink(idx)}
                >
                  <X className="h-3 w-3" />
                </button>
              </div>
            ))}
          </div>
        )}

        {/* ── Text / Checklist content ───────────────────────────────────── */}
        <div className="mt-4 flex flex-1 flex-col">
          {note.type === 'text' ? (
            <div className="flex flex-1 flex-col">
              {showMarkdownPreview && isMarkdown ? (
                <div className="prose prose-sm dark:prose-invert max-w-none flex-1 rounded-lg border border-border/40 bg-muted/20 p-4">
                  <MarkdownContent content={content || ''} />
                </div>
              ) : (
                <textarea
                  dir="auto"
                  className="flex-1 w-full resize-none bg-transparent text-sm leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/40"
                  placeholder={isMarkdown
                    ? t('notes.takeNoteMarkdown') 
                    : t('notes.takeNote') 
                  }
                  value={content}
                  onChange={(e) => { changeContent(e.target.value); scheduleSave() }}
                  style={{ minHeight: '200px' }}
                />
              )}

              {/* Ghost tag suggestions are now shown in the top labels strip */}
            </div>
          ) : (
            /* Checklist */
            <div className="space-y-1">
              {checkItems.filter((ci) => !ci.checked).map((ci, index) => (
                <div key={ci.id} className="group flex items-center gap-2 rounded-lg px-2 py-1 transition-colors hover:bg-muted/30">
                  <button type="button"
                    className="flex h-4 w-4 shrink-0 items-center justify-center rounded border border-border/60 transition-colors hover:border-primary"
                    onClick={() => handleToggleCheckItem(ci.id)}
                  />
                  <input
                    dir="auto"
                    className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground/40"
                    value={ci.text}
                    placeholder={t('notes.listItem') }
                    onChange={(e) => handleUpdateCheckText(ci.id, e.target.value)}
                  />
                  <button type="button" className="opacity-0 group-hover:opacity-100 transition-opacity" onClick={() => handleRemoveCheckItem(ci.id)}>
                    <X className="h-3.5 w-3.5 text-muted-foreground/60" />
                  </button>
                </div>
              ))}

              <button type="button"
                className="flex items-center gap-2 px-2 py-1 text-sm text-muted-foreground/60 hover:text-foreground"
                onClick={handleAddCheckItem}
              >
                <Plus className="h-4 w-4" />
                {t('notes.addItem') }
              </button>

              {checkItems.filter((ci) => ci.checked).length > 0 && (
                <div className="mt-3">
                  <p className="mb-1 px-2 text-xs text-muted-foreground/40 uppercase tracking-wider">
                    {t('notes.completedLabel')} ({checkItems.filter((ci) => ci.checked).length})
                  </p>
                  {checkItems.filter((ci) => ci.checked).map((ci) => (
                    <div key={ci.id} className="group flex items-center gap-2 rounded-lg px-2 py-1 text-muted-foreground transition-colors hover:bg-muted/20">
                      <button type="button"
                        className="flex h-4 w-4 shrink-0 items-center justify-center rounded border border-border/40 bg-muted/40"
                        onClick={() => handleToggleCheckItem(ci.id)}
                      >
                        <CheckSquare className="h-3 w-3 opacity-60" />
                      </button>
                      <span dir="auto" className="flex-1 text-sm line-through">{ci.text}</span>
                      <button type="button" className="opacity-0 group-hover:opacity-100 transition-opacity" onClick={() => handleRemoveCheckItem(ci.id)}>
                        <X className="h-3.5 w-3.5" />
                      </button>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
        </div>
      </div>

      {/* ── Memory Echo Connections Section ── */}
      <EditorConnectionsSection
        noteId={note.id}
        onOpenNote={(connNoteId) => {
          window.open(`/?note=${connNoteId}`, '_blank')
        }}
        onCompareNotes={handleCompareNotes}
        onMergeNotes={handleMergeNotes}
      />

      {/* ── Footer ───────────────────────────────────────────────────────────── */}
      <div className="shrink-0 border-t border-border/20 px-8 py-2">
        <div className="flex items-center gap-3 text-[11px] text-muted-foreground/40">
          <span>{t('notes.modified') } {formatDistanceToNow(new Date(note.updatedAt), { addSuffix: true, locale: dateLocale })}</span>
          <span>·</span>
          <span>{t('notes.created') || 'Créée'} {formatDistanceToNow(new Date(note.createdAt), { addSuffix: true, locale: dateLocale })}</span>
        </div>
      </div>

      {/* Fusion Modal */}
      {fusionNotes.length > 0 && (
        <FusionModal
          isOpen={fusionNotes.length > 0}
          onClose={() => setFusionNotes([])}
          notes={fusionNotes}
          onConfirmFusion={handleConfirmFusion}
        />
      )}

      {/* Comparison Modal */}
      {comparisonNotes.length > 0 && (
        <ComparisonModal
          isOpen={comparisonNotes.length > 0}
          onClose={() => setComparisonNotes([])}
          notes={comparisonNotes}
        />
      )}
      </div>

      {/* ── AI Copilot Side Panel ── */}
      {aiOpen && (
        <ContextualAIChat
          onClose={() => setAiOpen(false)}
          noteTitle={title}
          noteContent={content}
          onApplyToNote={(newContent) => {
            setPreviousContent(content)
            changeContent(newContent)
            scheduleSave()
          }}
          onUndoLastAction={previousContent !== null ? () => {
            changeContent(previousContent)
            setPreviousContent(null)
            scheduleSave()
          } : undefined}
          lastActionApplied={previousContent !== null}
          notebooks={notebooks.map(nb => ({ id: nb.id, name: nb.name }))}
        />
      )}
    </div>
  )
}
