'use client'

import { useEffect, useRef, useState, useCallback, forwardRef, useImperativeHandle } from 'react'
import { createPortal } from 'react-dom'
import { useLanguage } from '@/lib/i18n'
import { useEditor, EditorContent } from '@tiptap/react'
import { BubbleMenu } from '@tiptap/react/menus'
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'
import Placeholder from '@tiptap/extension-placeholder'
import TiptapLink from '@tiptap/extension-link'
import Highlight from '@tiptap/extension-highlight'
import Image from '@tiptap/extension-image'
import TextAlign from '@tiptap/extension-text-align'
import TaskList from '@tiptap/extension-task-list'
import TaskItem from '@tiptap/extension-task-item'
import { Table } from '@tiptap/extension-table'
import { TableRow } from '@tiptap/extension-table-row'
import { TableCell } from '@tiptap/extension-table-cell'
import { TableHeader } from '@tiptap/extension-table-header'
import Superscript from '@tiptap/extension-superscript'
import Subscript from '@tiptap/extension-subscript'
import Typography from '@tiptap/extension-typography'
import { ChartExtension } from './tiptap-chart-extension'
import { ChartSuggestionsDialog } from './chart-suggestions-dialog'
import type { Editor } from '@tiptap/core'
import type { EditorState } from '@tiptap/pm/state'
import {
  Bold, Italic, Underline as UnderlineIcon, Strikethrough, Code,
  Heading1, Heading2, Heading3, List, ListOrdered, CheckSquare,
  Quote, CodeXml, Minus, ImageIcon, Type, Highlighter, Link as LinkIcon,
  Sparkles, Wand2, Scissors, Lightbulb, X, Check, ExternalLink,
  FileText, Pilcrow, MessageSquare, AlignLeft, AlignCenter, AlignRight,
  Superscript as SuperscriptIcon, Subscript as SubscriptIcon, Expand, Plus,
  SpellCheck, Languages, BookOpen, Presentation, BarChart3
} from 'lucide-react'
import { cn } from '@/lib/utils'
import { toast } from 'sonner'

export interface RichTextEditorHandle {
  getEditor: () => Editor | null
}

interface RichTextEditorProps {
  content?: string
  onChange?: (content: string) => void
  className?: string
  placeholder?: string
  onImageUpload?: (file: File) => Promise<string>
}

type SlashItem = {
  title: string
  description: string
  icon: any
  category?: string
  shortcut?: string
  isImage?: boolean
  isAi?: boolean
  aiOption?: 'clarify' | 'shorten' | 'improve'
  command: (editor: Editor, range?: any) => void
}

type SlashCategoryId = 'basic' | 'media' | 'formatting' | 'ai'

type SlashMenuItem = SlashItem & { categoryId: SlashCategoryId }

const ORDERED_SLASH_CATEGORIES: SlashCategoryId[] = ['basic', 'media', 'formatting', 'ai']

function slashCategoryLabel(id: SlashCategoryId, t: (key: string) => string): string {
  switch (id) {
    case 'basic': return t('richTextEditor.slashCatBasic')
    case 'media': return t('richTextEditor.slashCatMedia')
    case 'formatting': return t('richTextEditor.slashCatFormatting')
    case 'ai': return t('richTextEditor.slashCatAi')
  }
}

/** Sent to /api/ai/reformulate as target language (unchanged for prompt compatibility). */
const TRANSLATE_TARGET_API_VALUES = ['Francais', 'English', 'Espanol', 'Deutsch', 'Persan', 'Portugais', 'Italiano', 'Chinois', 'Japonais'] as const

const AI_REFORMULATE_FALLBACK = '__RICH_TEXT_AI_FALLBACK__'

const CustomImage = Image.extend({
  addAttributes() {
    return {
      ...this.parent?.(),
      width: {
        default: '100%',
        parseHTML: element => element.style.width || element.getAttribute('width') || '100%',
        renderHTML: attributes => {
          if (!attributes.width) return {}
          return { style: `width: ${attributes.width}; max-width: 100%; height: auto;` }
        }
      }
    }
  }
})

const slashCommands: SlashItem[] = [
  // Basic blocks
  { title: 'Text', description: 'Plain paragraph', icon: Pilcrow, category: 'Basic blocks', shortcut: '¶', command: (e) => e.chain().focus().setParagraph().run() },
  { title: 'Heading 1', description: 'Big section heading', icon: Heading1, category: 'Basic blocks', shortcut: '#', command: (e) => e.chain().focus().toggleHeading({ level: 1 }).run() },
  { title: 'Heading 2', description: 'Medium section heading', icon: Heading2, category: 'Basic blocks', shortcut: '##', command: (e) => e.chain().focus().toggleHeading({ level: 2 }).run() },
  { title: 'Heading 3', description: 'Small section heading', icon: Heading3, category: 'Basic blocks', shortcut: '###', command: (e) => e.chain().focus().toggleHeading({ level: 3 }).run() },
  { title: 'Table', description: 'Insert a simple table', icon: () => <span className="text-xs font-bold border rounded px-1">TBL</span>, category: 'Basic blocks', command: (e) => e.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run() },
  { title: 'Bullet List', description: 'Unordered list', icon: List, category: 'Basic blocks', shortcut: '-', command: (e) => e.chain().focus().toggleBulletList().run() },
  { title: 'Numbered List', description: 'Ordered numbered list', icon: ListOrdered, category: 'Basic blocks', shortcut: '1.', command: (e) => e.chain().focus().toggleOrderedList().run() },
  { title: 'To-do List', description: 'Checkboxes for tasks', icon: CheckSquare, category: 'Basic blocks', shortcut: '[]', command: (e) => e.chain().focus().toggleTaskList().run() },
  { title: 'Quote', description: 'Capture a quote', icon: Quote, category: 'Basic blocks', shortcut: '>', command: (e) => e.chain().focus().toggleBlockquote().run() },
  { title: 'Code Block', description: 'Code snippet', icon: CodeXml, category: 'Basic blocks', shortcut: '```', command: (e) => e.chain().focus().toggleCodeBlock().run() },
  { title: 'Divider', description: 'Horizontal separator', icon: Minus, category: 'Basic blocks', shortcut: '---', command: (e) => e.chain().focus().setHorizontalRule().run() },
  // Media
  { title: 'Image', description: 'Embed image from URL', icon: ImageIcon, category: 'Media', isImage: true, command: () => { } },
  // Formatting
  { title: 'Align Left', description: 'Align text left', icon: AlignLeft, category: 'Formatting', command: (e) => e.chain().focus().setTextAlign('left').run() },
  { title: 'Align Center', description: 'Center text', icon: AlignCenter, category: 'Formatting', command: (e) => e.chain().focus().setTextAlign('center').run() },
  { title: 'Align Right', description: 'Align text right', icon: AlignRight, category: 'Formatting', command: (e) => e.chain().focus().setTextAlign('right').run() },
  // IA Note
  { title: 'Clarifier', description: 'Rendre le texte plus clair', icon: Lightbulb, category: 'IA Note', isAi: true, aiOption: 'clarify', command: () => { } },
  { title: 'Raccourcir', description: 'Condenser le texte', icon: Scissors, category: 'IA Note', isAi: true, aiOption: 'shorten', command: () => { } },
  { title: 'Améliorer', description: 'Améliorer le style', icon: Wand2, category: 'IA Note', isAi: true, aiOption: 'improve', command: () => { } },
  { title: 'Développer', description: 'Élaborer et enrichir le texte', icon: Expand, category: 'IA Note', isAi: true, aiOption: 'clarify', command: () => { } },
  // Formatting extensions
  { title: 'Bold', description: 'Make text bold', icon: Bold, category: 'Formatting', command: (e) => e.chain().focus().toggleBold().run() },
  { title: 'Italic', description: 'Make text italic', icon: Italic, category: 'Formatting', command: (e) => e.chain().focus().toggleItalic().run() },
  { title: 'Underline', description: 'Underline text', icon: UnderlineIcon, category: 'Formatting', command: (e) => e.chain().focus().toggleUnderline().run() },
  { title: 'Strike', description: 'Strikethrough text', icon: Strikethrough, category: 'Formatting', command: (e) => e.chain().focus().toggleStrike().run() },
  { title: 'Highlight', description: 'Highlight text', icon: Highlighter, category: 'Formatting', command: (e) => e.chain().focus().toggleHighlight().run() },
  { title: 'Superscript', description: 'Text above the baseline', icon: SuperscriptIcon, category: 'Formatting', command: (e) => e.chain().focus().toggleSuperscript().run() },
  { title: 'Subscript', description: 'Text below the baseline', icon: SubscriptIcon, category: 'Formatting', command: (e) => e.chain().focus().toggleSubscript().run() },
  // AI Tools
  {
    title: 'Diagramme', description: 'Générer un diagramme Excalidraw', icon: BookOpen, category: 'IA Note', command: (e) => {
      const event = new CustomEvent('memento-open-ai', { detail: { tab: 'actions', scroll: 'diagram' } })
      window.dispatchEvent(event)
    }
  },
  {
    title: 'Présentation', description: 'Générer des slides HTML/PPTX', icon: Presentation, category: 'IA Note', command: (e) => {
      const event = new CustomEvent('memento-open-ai', { detail: { tab: 'actions', scroll: 'slides' } })
      window.dispatchEvent(event)
    }
  },
  {
    title: 'Suggest Charts', description: 'AI suggère des graphiques basés sur votre contenu', icon: BarChart3, category: 'IA Note', isAi: true, command: (e) => {
      // Handler will be called by SlashCommandMenu
    }
  },
]

async function aiReformulate(text: string, option: string, language?: string): Promise<string> {
  const res = await fetch('/api/ai/reformulate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text, option, format: 'html', language }),
  })
  const data = await res.json()
  if (!res.ok) {
    if (data?.errorKey === 'ai.wordCountMin') {
      throw new Error(t('ai.wordCountMin') || `Minimum ${data?.params?.min || 10} mots requis (${data?.params?.current || 0} actuels)`)
    }
    if (data?.errorKey === 'ai.wordCountMax') {
      throw new Error(t('ai.wordCountMax') || `Maximum ${data?.params?.max || 500} mots (${data?.params?.current || 0} actuels)`)
    }
    if (data?.errorKey === 'ai.featureLocked') {
      throw new Error(t('ai.featureLocked') || 'Cette fonctionnalité nécessite le plan PRO.')
    }
    if (data?.errorKey === 'ai.quotaExceeded') {
      throw new Error(t('ai.quotaExceeded') || 'Limite mensuelle atteinte.')
    }
    if (data?.quotaExceeded) {
      throw new Error(t('ai.quotaExceeded') || 'Limite mensuelle atteinte.')
    }
    const serverMsg = typeof data?.error === 'string' && !data.error.includes(':') ? data.error.trim() : AI_REFORMULATE_FALLBACK
    throw new Error(serverMsg)
  }
  return data.reformulatedText || data.text || text
}

function useImageInsert() {
  const [open, setOpen] = useState(false)
  const editorRef = useRef<Editor | null>(null)

  const requestInsert = useCallback((editor: Editor) => {
    editorRef.current = editor
    setOpen(true)
  }, [])

  const confirm = useCallback((url: string) => {
    if (url.trim() && editorRef.current) {
      editorRef.current.chain().focus().setImage({ src: url.trim() }).run()
    }
    setOpen(false)
    editorRef.current = null
  }, [])

  const cancel = useCallback(() => {
    setOpen(false)
    editorRef.current = null
  }, [])

  return { open, requestInsert, confirm, cancel }
}

export const RichTextEditor = forwardRef<RichTextEditorHandle, RichTextEditorProps>(
  function RichTextEditor({ content, onChange, className, placeholder, onImageUpload }, ref) {
    const { t } = useLanguage()
    const imageInsert = useImageInsert()

    const editor = useEditor({
      extensions: [
        StarterKit.configure({ heading: { levels: [1, 2, 3] }, link: false, underline: false }),
        Underline,
        TiptapLink.configure({ openOnClick: false, autolink: true }),
        Highlight.configure({ multicolor: false }),
        CustomImage.configure({ inline: false, allowBase64: true }),
        TextAlign.configure({ types: ['heading', 'paragraph', 'image'] }),
        TaskList,
        TaskItem.configure({ nested: true }),
        Table.configure({ resizable: true }),
        TableRow,
        TableHeader,
        TableCell,
        Superscript,
        Subscript,
        Typography,
        ChartExtension,
        Placeholder.configure({ placeholder: placeholder || t('richTextEditor.placeholder') || "Tapez '/' pour voir les commandes..." }),
      ],
      content: content || '',
      immediatelyRender: false,
      editorProps: {
        attributes: { class: 'notion-editor' },
        handlePaste: (view, event, slice) => {
          if (!onImageUpload) return false;
          const items = Array.from(event.clipboardData?.items || []);
          const hasImage = items.some(item => item.type.startsWith('image/'));
          if (!hasImage) return false;
          event.preventDefault();
          const images = items.filter(item => item.type.startsWith('image/')).map(item => item.getAsFile()).filter(f => f !== null) as File[];
          images.forEach(async (file) => {
            try {
              toast.info(t('notes.uploading'));
              const url = await onImageUpload(file);
              const { schema } = view.state;
              const node = schema.nodes.image.create({ src: url });
              const tr = view.state.tr.replaceSelectionWith(node);
              view.dispatch(tr);
            } catch (err) {
              toast.error(t('notes.uploadFailed'));
            }
          });
          return true;
        }
      },
      onUpdate: ({ editor: e }) => {
        const html = e.getHTML()
        lastEmittedContent.current = html
        onChange?.(html)
      },
    })

    const lastEmittedContent = useRef<string>(content || '')

    // Chart suggestions dialog state
    const [chartSuggestionsOpen, setChartSuggestionsOpen] = useState(false)
    const [currentNoteContent, setCurrentNoteContent] = useState(content || '')

    useEffect(() => {
      if (editor && content !== undefined && content !== lastEmittedContent.current) {
        editor.commands.setContent(content || '')
        lastEmittedContent.current = content || ''
      }
      // Update current note content for chart suggestions
      if (content !== undefined) {
        setCurrentNoteContent(content || '')
      }
    }, [content, editor])

    useImperativeHandle(ref, () => ({ getEditor: () => editor }), [editor])

    // Chart suggestion handlers
    const handleOpenChartSuggestions = useCallback(() => {
      if (!editor || !editor.isEditable) return

      // Get current selection text if any
      const { from, to, empty } = editor.state.selection
      const selectionText = !empty ? editor.state.doc.textBetween(from, to, ' ') : null

      setChartSuggestionsOpen(true)
    }, [editor])

    const handleSelectChart = useCallback((chartContent: string) => {
      if (!editor || !editor.isEditable) return

      try {
        console.log('[handleSelectChart] Inserting chart type:', chartContent.split('\n')[0])

        const { from } = editor.state.selection
        const { tr } = editor.state
        const { schema } = editor.state

        // Create the chartBlock node directly
        const chartNode = schema.nodes.chartBlock.create({
          code: chartContent,
          language: 'chart'
        })

        if (!chartNode) {
          console.error('[handleSelectChart] Failed to create chartBlock node')
          toast.error('Chart extension not available')
          return
        }

        // Insert the chart node at current position
        const transaction = tr.insert(from, chartNode)

        // Get the position after the inserted chart
        const insertPos = transaction.mapping.map(from)
        const chartSize = chartNode.nodeSize

        // Add a paragraph after for continued typing
        const paragraph = schema.nodes.paragraph.create()
        transaction.insert(insertPos + chartSize, paragraph)

        editor.view.dispatch(transaction)
        editor.chain().focus().run()

        console.log('[handleSelectChart] Chart inserted successfully')
      } catch (error) {
        console.error('[handleSelectChart] Failed:', error)
        toast.error('Failed to insert chart: ' + (error as Error).message)
      }
    }, [editor])

    return (
      <div className={cn('notion-editor-wrapper', className)}>
        {editor && (
          <BubbleMenu
            editor={editor}
            className="notion-bubble-menu"
            {...({
              tippyOptions: {
                appendTo: () => document.body,
                zIndex: 99999,
                fallbackPlacements: ['bottom', 'top']
              }
            } as any)}
            shouldShow={({ editor: e, state }: { editor: Editor; state: EditorState }) => {
              const { from, to } = state.selection
              const isImage = e.isActive('image')
              return (from !== to && !e.isActive('codeBlock')) || isImage
            }}
          >
            <BubbleToolbar editor={editor} />
          </BubbleMenu>
        )}

        {editor && <SlashCommandMenu editor={editor} onInsertImage={imageInsert.requestInsert} onSuggestCharts={handleOpenChartSuggestions} />}

        <EditorContent editor={editor} />

        {imageInsert.open && (
          <ImageModal onConfirm={imageInsert.confirm} onCancel={imageInsert.cancel} />
        )}

        {chartSuggestionsOpen && (
          <ChartSuggestionsDialog
            isOpen={chartSuggestionsOpen}
            content={currentNoteContent}
            selection={editor.state.selection.empty ? null : editor.state.doc.textBetween(editor.state.selection.from, editor.state.selection.to, ' ')}
            onClose={() => setChartSuggestionsOpen(false)}
            onSelectChart={handleSelectChart}
          />
        )}
      </div>
    )
  }
)

function ImageModal({ onConfirm, onCancel }: { onConfirm: (url: string) => void; onCancel: () => void }) {
  const { t } = useLanguage()
  const [url, setUrl] = useState('')
  const [preview, setPreview] = useState<string | null>(null)
  const [error, setError] = useState('')
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => { inputRef.current?.focus() }, [])

  const handleConfirm = () => {
    if (!url.trim()) return
    if (!/^https?:\/\/.+/i.test(url.trim())) { setError(t('richTextEditor.imageModalInvalidUrl')); return }
    onConfirm(url.trim())
  }

  return (
    <div className="notion-overlay" onClick={onCancel}>
      <div className="notion-image-modal" onClick={(e) => e.stopPropagation()}>
        <div className="text-sm font-medium mb-3">{t('richTextEditor.imageModalTitle')}</div>
        <input
          ref={inputRef}
          type="url"
          value={url}
          onChange={(e) => { setUrl(e.target.value); setError(''); setPreview(null) }}
          onKeyDown={(e) => { if (e.key === 'Enter') handleConfirm(); if (e.key === 'Escape') onCancel() }}
          placeholder={t('richTextEditor.imageUrlPlaceholder')}
          className="notion-modal-input"
        />
        {url.trim() && !preview && (
          <button onClick={() => setPreview(url.trim())} className="text-xs text-muted-foreground hover:text-foreground transition-colors underline mt-2">
            {t('richTextEditor.imageModalPreview')}
          </button>
        )}
        {preview && (
          <div className="mt-2 rounded-lg overflow-hidden border border-border max-h-40">
            <img src={preview} alt="" className="max-h-40 object-contain w-full" onError={() => { setPreview(null); setError(t('richTextEditor.imageModalLoadFailed')) }} />
          </div>
        )}
        {error && <div className="mt-1.5 text-xs text-destructive">{error}</div>}
        <div className="flex justify-end gap-2 mt-4">
          <button onClick={onCancel} className="notion-modal-btn">{t('richTextEditor.imageModalCancel')}</button>
          <button onClick={handleConfirm} className="notion-modal-btn notion-modal-btn-primary">{t('richTextEditor.imageModalInsert')}</button>
        </div>
      </div>
    </div>
  )
}

function BubbleToolbar({ editor }: { editor: Editor | null }) {
  const { t, language } = useLanguage()

  const toastAiError = (err: unknown) => {
    const msg = err instanceof Error ? err.message : ''
    toast.error(msg && msg !== AI_REFORMULATE_FALLBACK ? msg : t('richTextEditor.aiReformulateFailed'))
  }

  const [, setTick] = useState(0)
  const [aiOpen, setAiOpen] = useState(false)
  const [aiLoading, setAiLoading] = useState(false)
  const [linkOpen, setLinkOpen] = useState(false)
  const [linkUrl, setLinkUrl] = useState('')
  const linkInputRef = useRef<HTMLInputElement>(null)
  const [translateOpen, setTranslateOpen] = useState(false)
  const [customLang, setCustomLang] = useState('')
  const [aiModal, setAiModal] = useState<{ type: 'explain' | 'preview'; origText: string; html: string; from: number; to: number } | null>(null)

  useEffect(() => {
    if (!editor) return
    const h = () => setTick(t => t + 1)
    editor.on('transaction', h)
    editor.on('selectionUpdate', h)
    return () => { editor.off('transaction', h); editor.off('selectionUpdate', h) }
  }, [editor])

  useEffect(() => {
    if (linkOpen && linkInputRef.current) linkInputRef.current.focus()
  }, [linkOpen])

  if (!editor) return null

  const marks = [
    { icon: Bold, active: editor.isActive('bold'), action: () => editor.chain().focus().toggleBold().run(), title: t('richTextEditor.bold') },
    { icon: Italic, active: editor.isActive('italic'), action: () => editor.chain().focus().toggleItalic().run(), title: t('richTextEditor.italic') },
    { icon: UnderlineIcon, active: editor.isActive('underline'), action: () => editor.chain().focus().toggleUnderline().run(), title: t('richTextEditor.underline') },
    { icon: Strikethrough, active: editor.isActive('strike'), action: () => editor.chain().focus().toggleStrike().run(), title: t('richTextEditor.strike') },
    { icon: Code, active: editor.isActive('code'), action: () => editor.chain().focus().toggleCode().run(), title: t('richTextEditor.code') },
    { icon: Highlighter, active: editor.isActive('highlight'), action: () => editor.chain().focus().toggleHighlight().run(), title: t('richTextEditor.highlight') },
    { icon: SuperscriptIcon, active: editor.isActive('superscript'), action: () => editor.chain().focus().toggleSuperscript().run(), title: t('richTextEditor.superscript') },
    { icon: SubscriptIcon, active: editor.isActive('subscript'), action: () => editor.chain().focus().toggleSubscript().run(), title: t('richTextEditor.subscript') },
  ]

  const handleAI = async (option: 'clarify' | 'shorten' | 'improve' | 'fix_grammar' | 'translate' | 'explain', targetLang?: string) => {
    const { from, to } = editor.state.selection
    const text = editor.state.doc.textBetween(from, to, ' ')
    if (!text || text.split(/\s+/).length < 2) return
    setAiLoading(true)
    setAiOpen(false)
    setTranslateOpen(false)
    try {
      const lang = option === 'translate' ? (targetLang || language) : language
      const result = await aiReformulate(text, option, lang)
      window.dispatchEvent(new Event('ai-usage-changed'))
      if (option === 'explain') {
        setAiModal({ type: 'explain', origText: text, html: result, from, to })
      } else {
        setAiModal({ type: 'preview', origText: text, html: result, from, to })
      }
    } catch (err) {
      console.error('AI error:', err)
      toastAiError(err)
    } finally {
      setAiLoading(false)
    }
  }

  const applyAiResult = () => {
    if (!aiModal || !editor) { setAiModal(null); return }
    const clean = aiModal.html.replace(/^<p>([\s\S]*)<\/p>$/, '$1').trim()
    editor.chain().focus().insertContentAt({ from: aiModal.from, to: aiModal.to }, clean).run()
    setAiModal(null)
  }

  const openLinkEditor = () => {
    const existing = editor.getAttributes('link').href || ''
    setLinkUrl(existing)
    setLinkOpen(true)
    setAiOpen(false)
  }

  const applyLink = () => {
    if (linkUrl.trim()) {
      editor.chain().focus().extendMarkRange('link').setLink({ href: linkUrl.trim() }).run()
    } else {
      editor.chain().focus().extendMarkRange('link').unsetLink().run()
    }
    setLinkOpen(false)
  }

  if (linkOpen) {
    return (
      <div className="flex items-center gap-1.5 px-2 py-1.5">
        <LinkIcon className="w-3.5 h-3.5 text-muted-foreground flex-shrink-0" />
        <input
          ref={linkInputRef}
          type="url"
          value={linkUrl}
          onChange={(e) => setLinkUrl(e.target.value)}
          onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); applyLink() }; if (e.key === 'Escape') { setLinkOpen(false); editor.chain().focus().run() } }}
          placeholder={t('richTextEditor.linkPlaceholder')}
          className="notion-inline-input"
        />
        <button onClick={applyLink} className="notion-bubble-btn notion-bubble-btn-active"><Check className="w-3.5 h-3.5" /></button>
        {editor.isActive('link') && (
          <button onClick={() => { editor.chain().focus().extendMarkRange('link').unsetLink().run(); setLinkOpen(false) }} className="notion-bubble-btn">
            <ExternalLink className="w-3.5 h-3.5" />
          </button>
        )}
        <button onClick={() => { setLinkOpen(false); editor.chain().focus().run() }} className="notion-bubble-btn"><X className="w-3.5 h-3.5" /></button>
      </div>
    )
  }

  return (
    <div className="flex items-center gap-0.5 px-1 py-0.5 relative">
      {marks.map((m, i) => (
        <button key={i} onClick={m.action} title={m.title} className={cn('notion-bubble-btn rounded-md', m.active && 'notion-bubble-btn-active')}>
          <m.icon className="w-3.5 h-3.5" />
        </button>
      ))}
      <div className="w-px h-4 bg-gray-300 dark:bg-gray-600 mx-0.5" />
      <button onClick={openLinkEditor} className={cn('notion-bubble-btn rounded-md', editor.isActive('link') && 'notion-bubble-btn-active')}><LinkIcon className="w-3.5 h-3.5" /></button>
      <button onClick={() => setAiOpen(!aiOpen)} className={cn('notion-bubble-btn rounded-md', aiLoading && 'animate-pulse')}><Sparkles className="w-3.5 h-3.5" /></button>
      {editor.isActive('image') && (
        <>
          <div className="w-px h-4 bg-gray-300 dark:bg-gray-600 mx-0.5" />
          <button onClick={() => editor.chain().focus().updateAttributes('image', { width: '25%' }).run()} className="notion-bubble-btn rounded-md text-xs font-medium px-1">25%</button>
          <button onClick={() => editor.chain().focus().updateAttributes('image', { width: '50%' }).run()} className="notion-bubble-btn rounded-md text-xs font-medium px-1">50%</button>
          <button onClick={() => editor.chain().focus().updateAttributes('image', { width: '100%' }).run()} className="notion-bubble-btn rounded-md text-xs font-medium px-1">100%</button>
        </>
      )}
      {aiOpen && (
        <div className="notion-ai-submenu">
          <button className="notion-ai-subitem" onClick={() => handleAI('clarify')}><Lightbulb className="w-3.5 h-3.5 text-amber-500" /><span>{t('richTextEditor.slashClarify')}</span></button>
          <button className="notion-ai-subitem" onClick={() => handleAI('shorten')}><Scissors className="w-3.5 h-3.5 text-primary" /><span>{t('richTextEditor.slashShorten')}</span></button>
          <button className="notion-ai-subitem" onClick={() => handleAI('improve')}><Wand2 className="w-3.5 h-3.5 text-purple-500" /><span>{t('richTextEditor.slashImprove')}</span></button>
          <button className="notion-ai-subitem" onClick={() => handleAI('fix_grammar')}><SpellCheck className="w-3.5 h-3.5 text-green-500" /><span>{t('ai.action.fixGrammar')}</span></button>
          <button className="notion-ai-subitem" onClick={() => setTranslateOpen(v => !v)}><Languages className="w-3.5 h-3.5 text-indigo-500" /><span>{t('ai.action.translate')}</span></button>
          {translateOpen && (
            <div className="notion-ai-lang-picker">
              {TRANSLATE_TARGET_API_VALUES.map((apiValue) => (
                <button key={apiValue} className="notion-ai-lang-item" onClick={() => handleAI('translate', apiValue)}>
                  {t(`richTextEditor.translateTargets.${apiValue}`)}
                </button>
              ))}
              <div className="flex items-center gap-1 px-1 pt-1 w-full border-t border-border/40 mt-1">
                <input
                  className="notion-inline-input text-xs flex-1 min-w-0"
                  placeholder={t('ai.action.customLang') || 'Autre...'}
                  value={customLang}
                  onChange={e => setCustomLang(e.target.value)}
                  onKeyDown={e => { if (e.key === 'Enter' && customLang.trim()) handleAI('translate', customLang.trim()) }}
                />
              </div>
            </div>
          )}
          <button className="notion-ai-subitem" onClick={() => handleAI('explain')}><BookOpen className="w-3.5 h-3.5 text-orange-500" /><span>{t('ai.action.explain')}</span></button>
        </div>
      )}
      {aiModal && (
        <div className="notion-ai-result-overlay" onClick={() => setAiModal(null)}>
          <div className="notion-ai-result-modal" onClick={e => e.stopPropagation()}>
            <div className="notion-ai-result-header">
              <span className="text-sm font-semibold">
                {aiModal.type === 'explain' ? t('ai.action.explain') : (t('ai.result.preview') || 'Apercu IA')}
              </span>
              <button onClick={() => setAiModal(null)} className="notion-bubble-btn ml-auto"><X className="w-3.5 h-3.5" /></button>
            </div>
            {aiModal.type === 'preview' && (
              <div className="notion-ai-result-section">
                <span className="notion-ai-result-label">{t('ai.result.original') || 'Original'}</span>
                <p className="text-sm text-muted-foreground line-through opacity-60 mt-1">{aiModal.origText}</p>
              </div>
            )}
            <div className="notion-ai-result-section">
              {aiModal.type === 'preview' && <span className="notion-ai-result-label">{t('ai.result.suggestion') || 'Suggestion'}</span>}
              <div className="prose prose-sm max-w-none text-sm mt-1" dangerouslySetInnerHTML={{ __html: aiModal.html }} />
            </div>
            <div className="flex justify-end gap-2 mt-3 pt-3 border-t border-border/40">
              <button onClick={() => setAiModal(null)} className="notion-modal-btn">{t('common.cancel') || 'Annuler'}</button>
              {aiModal.type === 'preview' && (
                <button onClick={applyAiResult} className="notion-modal-btn notion-modal-btn-primary">{t('ai.result.apply') || 'Appliquer'}</button>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  )
}

function SlashCommandMenu({ editor, onInsertImage, onSuggestCharts }: { editor: Editor; onInsertImage: (editor: Editor) => void; onSuggestCharts: () => void }) {
  const { t } = useLanguage()
  const [isOpen, setIsOpen] = useState(false)
  const [query, setQuery] = useState('')
  const [selectedIndex, setSelectedIndex] = useState(0)
  const [activeCategory, setActiveCategory] = useState<SlashCategoryId | null>(null)
  const [coords, setCoords] = useState({ top: 0, left: 0 })
  const [aiLoading, setAiLoading] = useState(false)
  const menuRef = useRef<HTMLDivElement>(null)
  const selectedItemRef = useRef<HTMLButtonElement>(null)
  const menuInteracting = useRef(false)

  const localCommands: SlashMenuItem[] = [
    { ...slashCommands[0], title: t('richTextEditor.slashText'), description: t('richTextEditor.slashTextDesc'), categoryId: 'basic' },
    { ...slashCommands[1], title: t('richTextEditor.slashH1'), description: t('richTextEditor.slashH1Desc'), categoryId: 'basic' },
    { ...slashCommands[2], title: t('richTextEditor.slashH2'), description: t('richTextEditor.slashH2Desc'), categoryId: 'basic' },
    { ...slashCommands[3], title: t('richTextEditor.slashH3'), description: t('richTextEditor.slashH3Desc'), categoryId: 'basic' },
    { ...slashCommands[4], title: t('richTextEditor.slashTable'), description: t('richTextEditor.slashTableDesc'), categoryId: 'basic' },
    { ...slashCommands[5], title: t('richTextEditor.slashBullet'), description: t('richTextEditor.slashBulletDesc'), categoryId: 'basic' },
    { ...slashCommands[6], title: t('richTextEditor.slashNumbered'), description: t('richTextEditor.slashNumberedDesc'), categoryId: 'basic' },
    { ...slashCommands[7], title: t('richTextEditor.slashTodo'), description: t('richTextEditor.slashTodoDesc'), categoryId: 'basic' },
    { ...slashCommands[8], title: t('richTextEditor.slashQuote'), description: t('richTextEditor.slashQuoteDesc'), categoryId: 'basic' },
    { ...slashCommands[9], title: t('richTextEditor.slashCode'), description: t('richTextEditor.slashCodeDesc'), categoryId: 'basic' },
    { ...slashCommands[10], title: t('richTextEditor.slashDivider'), description: t('richTextEditor.slashDividerDesc'), categoryId: 'basic' },
    { ...slashCommands[11], title: t('richTextEditor.slashImage'), description: t('richTextEditor.slashImageDesc'), categoryId: 'media' },
    { ...slashCommands[12], title: t('richTextEditor.slashAlignLeft'), description: t('richTextEditor.slashAlignLeftDesc'), categoryId: 'formatting' },
    { ...slashCommands[13], title: t('richTextEditor.slashAlignCenter'), description: t('richTextEditor.slashAlignCenterDesc'), categoryId: 'formatting' },
    { ...slashCommands[14], title: t('richTextEditor.slashAlignRight'), description: t('richTextEditor.slashAlignRightDesc'), categoryId: 'formatting' },
    { ...slashCommands[15], title: t('richTextEditor.slashClarify'), description: t('richTextEditor.slashClarifyDesc'), categoryId: 'ai' },
    { ...slashCommands[16], title: t('richTextEditor.slashShorten'), description: t('richTextEditor.slashShortenDesc'), categoryId: 'ai' },
    { ...slashCommands[17], title: t('richTextEditor.slashImprove'), description: t('richTextEditor.slashImproveDesc'), categoryId: 'ai' },
    { ...slashCommands[18], title: t('richTextEditor.slashExpand'), description: t('richTextEditor.slashExpandDesc'), categoryId: 'ai' },
    { ...slashCommands[19], title: t('richTextEditor.bold'), description: t('richTextEditor.bold'), categoryId: 'formatting' },
    { ...slashCommands[20], title: t('richTextEditor.italic'), description: t('richTextEditor.italic'), categoryId: 'formatting' },
    { ...slashCommands[21], title: t('richTextEditor.underline'), description: t('richTextEditor.underline'), categoryId: 'formatting' },
    { ...slashCommands[22], title: t('richTextEditor.strike'), description: t('richTextEditor.strike'), categoryId: 'formatting' },
    { ...slashCommands[23], title: t('richTextEditor.highlight'), description: t('richTextEditor.highlight'), categoryId: 'formatting' },
    { ...slashCommands[24], title: t('richTextEditor.slashSuperscript'), description: t('richTextEditor.slashSuperscriptDesc'), categoryId: 'formatting' },
    { ...slashCommands[25], title: t('richTextEditor.slashSubscript'), description: t('richTextEditor.slashSubscriptDesc'), categoryId: 'formatting' },
    { ...slashCommands[26], title: t('richTextEditor.slashDiagram'), description: t('richTextEditor.slashDiagramDesc'), categoryId: 'ai' },
    { ...slashCommands[27], title: t('richTextEditor.slashSlides'), description: t('richTextEditor.slashSlidesDesc'), categoryId: 'ai' },
    { ...slashCommands[28], title: 'Suggest Charts', description: 'AI suggère des graphiques basés sur votre contenu', categoryId: 'ai' },
  ]

  const closeMenu = useCallback(() => {
    setIsOpen(false); setQuery(''); setSelectedIndex(0); setActiveCategory(null)
  }, [])

  const deleteSlashText = useCallback(() => {
    const { from, to } = editor.state.selection
    const textBefore = editor.state.doc.textBetween(Math.max(0, from - 50), from, '\n')
    const slashIdx = textBefore.lastIndexOf('/')
    if (slashIdx >= 0) {
      const deleteFrom = from - (textBefore.length - slashIdx)
      editor.chain().focus().deleteRange({ from: deleteFrom, to }).run()
    }
  }, [editor])

  const handleSelect = useCallback(async (item: SlashMenuItem) => {
    const toastAi = (err: unknown) => {
      const msg = err instanceof Error ? err.message : ''
      toast.error(msg && msg !== AI_REFORMULATE_FALLBACK ? msg : t('richTextEditor.aiReformulateFailed'))
    }
    if (item.isImage) {
      deleteSlashText(); closeMenu(); onInsertImage(editor)
    } else if (item.isAi && item.aiOption) {
      deleteSlashText(); closeMenu(); setAiLoading(true)
      try {
        const allText = editor.state.doc.textContent
        if (!allText || allText.split(/\s+/).length < 5) return
        const result = await aiReformulate(allText, item.aiOption)
        editor.chain().focus().setContent(result).run()
      } catch (err) {
        console.error('AI slash error:', err)
        toastAi(err)
      }
      finally { setAiLoading(false) }
    } else if (item.title === 'Suggest Charts') {
      deleteSlashText(); closeMenu(); onSuggestCharts()
    } else {
      deleteSlashText(); item.command(editor); closeMenu()
    }
  }, [editor, closeMenu, deleteSlashText, onInsertImage, onSuggestCharts, t])

  const presentCategoryIds = new Set(localCommands.map(c => c.categoryId))
  const allCategories = ORDERED_SLASH_CATEGORIES.filter(id => presentCategoryIds.has(id))

  const textFiltered = localCommands.filter(c => c.title.toLowerCase().includes(query.toLowerCase()) || c.description.toLowerCase().includes(query.toLowerCase()))
  const filtered = activeCategory ? textFiltered.filter(c => c.categoryId === activeCategory) : textFiltered

  const availableCategoriesInSearch = textFiltered.reduce((acc, item) => {
    const id = item.categoryId
    if (!acc[id]) acc[id] = []
    acc[id].push(item)
    return acc
  }, {} as Record<SlashCategoryId, SlashMenuItem[]>)

  const categories = filtered.reduce((acc, item) => {
    const id = item.categoryId
    if (!acc[id]) acc[id] = []
    acc[id].push(item)
    return acc
  }, {} as Record<SlashCategoryId, SlashMenuItem[]>)

  // Auto-scroll selected item into view
  useEffect(() => {
    selectedItemRef.current?.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
  }, [selectedIndex])

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (!isOpen) return
      if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedIndex(i => (i + 1) % filtered.length); return }
      if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedIndex(i => (i - 1 + filtered.length) % filtered.length); return }
      if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
        e.preventDefault()
        const availableTabs = [null as SlashCategoryId | null, ...allCategories.filter(id => (availableCategoriesInSearch[id]?.length ?? 0) > 0)]
        const currentIndex = availableTabs.indexOf(activeCategory)
        const nextIndex = e.key === 'ArrowRight'
          ? (currentIndex + 1) % availableTabs.length
          : (currentIndex - 1 + availableTabs.length) % availableTabs.length
        setActiveCategory(availableTabs[nextIndex])
        setSelectedIndex(0)
        return
      }
      if (e.key === 'Enter') {
        e.preventDefault()
        const item = filtered[selectedIndex]
        if (item) handleSelect(item)
        return
      }
      if (e.key === 'Escape') { e.preventDefault(); closeMenu(); return }
      if (e.key === 'Tab') {
        e.preventDefault()
        const availableTabs = [null as SlashCategoryId | null, ...allCategories.filter(id => (availableCategoriesInSearch[id]?.length ?? 0) > 0)]
        const nextIndex = (availableTabs.indexOf(activeCategory) + 1) % availableTabs.length
        setActiveCategory(availableTabs[nextIndex])
        setSelectedIndex(0)
        return
      }
    }
    document.addEventListener('keydown', handleKeyDown, true)
    return () => document.removeEventListener('keydown', handleKeyDown, true)
  }, [isOpen, selectedIndex, filtered, handleSelect, closeMenu, activeCategory, allCategories, availableCategoriesInSearch])

  useEffect(() => {
    if (!isOpen) return
    const { from } = editor.state.selection
    const c = editor.view.coordsAtPos(from)

    // Check if menu would overflow bottom
    const menuHeight = menuRef.current?.offsetHeight || 300
    const wouldOverflow = c.bottom + menuHeight + 20 > window.innerHeight

    if (wouldOverflow) {
      setCoords({ top: c.top - menuHeight - 8, left: c.left })
    } else {
      setCoords({ top: c.bottom + 8, left: c.left })
    }
  }, [isOpen, editor, query, filtered.length])

  useEffect(() => {
    const handleClick = (e: MouseEvent) => {
      if (isOpen && menuRef.current && !menuRef.current.contains(e.target as Node)) {
        closeMenu()
      }
    }
    document.addEventListener('click', handleClick)
    return () => document.removeEventListener('click', handleClick)
  }, [isOpen, closeMenu])

  useEffect(() => {
    const handler = () => {
      // Ignore events fired while user clicks inside the menu
      if (menuInteracting.current) return
      const { from, empty } = editor.state.selection
      if (!empty) { if (isOpen) closeMenu(); return }
      const text = editor.state.doc.textBetween(Math.max(0, from - 50), from, '\n')
      const m = text.match(/\/([^\s/]*)$/)
      if (m) {
        setQuery(m[1])
        setSelectedIndex(0)
        if (!isOpen) { setIsOpen(true); setActiveCategory(null) } // reset category filter on fresh open
      }
      else if (isOpen) closeMenu()
    }
    editor.on('update', handler)
    editor.on('selectionUpdate', handler)
    return () => { editor.off('update', handler); editor.off('selectionUpdate', handler) }
  }, [editor, isOpen, closeMenu])

  if (!isOpen || filtered.length === 0) return null

  let flatIndex = -1
  const sectionIds = ORDERED_SLASH_CATEGORIES.filter(id => (categories[id]?.length ?? 0) > 0)
  const totalVisible = sectionIds.length

  return createPortal(
    <div
      ref={menuRef}
      className="notion-slash-menu"
      style={{ top: coords.top, left: coords.left }}
      onClick={(e) => e.stopPropagation()}
    >
      {/* Category tabs */}
      {!query && totalVisible > 1 && (
        <div className="notion-slash-tabs" onMouseDown={(e) => e.preventDefault()}>
          <button
            className={cn('notion-slash-tab', !activeCategory && 'notion-slash-tab-active')}
            onMouseDown={(e) => e.preventDefault()}
            onClick={() => { setActiveCategory(null); setSelectedIndex(0) }}
          >
            {t('richTextEditor.slashTabAll')}
          </button>
          {allCategories.filter(id => (availableCategoriesInSearch[id]?.length ?? 0) > 0).map(catId => (
            <button
              key={catId}
              className={cn('notion-slash-tab', activeCategory === catId && 'notion-slash-tab-active', catId === 'ai' && 'notion-slash-tab-ai')}
              onMouseDown={(e) => e.preventDefault()}
              onClick={() => { setActiveCategory(activeCategory === catId ? null : catId); setSelectedIndex(0) }}
            >
              {catId === 'ai' && <Sparkles className="w-2.5 h-2.5" />}
              {slashCategoryLabel(catId, t)}
              <span className="notion-slash-tab-count">{availableCategoriesInSearch[catId]?.length ?? 0}</span>
            </button>
          ))}
        </div>
      )}

      {/* Header hint */}
      <div className="notion-slash-header">
        <span>{t('richTextEditor.slashHint')}</span>
      </div>

      {aiLoading && (
        <div className="notion-slash-loading">
          <Sparkles className="w-3.5 h-3.5 animate-spin" />
          <span>{t('richTextEditor.slashLoading')}</span>
        </div>
      )}
      {!aiLoading && sectionIds.map((catId) => {
        const items = categories[catId]!
        return (
          <div key={catId} className="notion-slash-section">
            <div className={cn('notion-slash-label', catId === 'ai' && 'notion-slash-label-ai')}>
              {catId === 'ai' && <Sparkles className="w-3 h-3 inline mr-1" />}
              {slashCategoryLabel(catId, t)}
            </div>
            {items.map((item) => {
              flatIndex++
              const idx = flatIndex
              const isSelected = idx === selectedIndex
              return (
                <button
                  key={`${catId}-${item.title}-${item.shortcut ?? ''}`}
                  ref={isSelected ? selectedItemRef : null}
                  className={cn('notion-slash-item', isSelected && 'notion-slash-item-selected')}
                  onMouseDown={(e) => e.preventDefault()}
                  onClick={() => handleSelect(item)}
                  onMouseEnter={() => setSelectedIndex(idx)}
                >
                  <div className={cn('notion-slash-icon', item.isAi && 'notion-slash-icon-ai')}>
                    <item.icon className="w-4 h-4" />
                  </div>
                  <div className="notion-slash-content">
                    <span className="notion-slash-title">{item.title}</span>
                    <span className="notion-slash-desc">{item.description}</span>
                  </div>
                  {item.shortcut && (
                    <span className="notion-slash-shortcut">{item.shortcut}</span>
                  )}
                </button>
              )
            })}
          </div>
        )
      })}
    </div>,
    document.body
  )
}





