import { useState, useRef, useEffect, useCallback } from 'react'
import { useI18n } from '../i18n'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import rehypeHighlight from 'rehype-highlight'
import 'katex/dist/katex.min.css'
import 'highlight.js/styles/github-dark.css'

const RANKS = {
  commandant: { label: 'Commandant', short: 'CDT', color: '#FFD740' },
  general:    { label: 'General',    short: 'GEN', color: '#FF9100' },
  colonel:    { label: 'Colonel',    short: 'COL', color: '#FF6D00' },
  lieutenant: { label: 'Lieutenant', short: 'LT',  color: '#448AFF' },
  soldat:     { label: 'Soldat',     short: 'SDT', color: '#00E676' },
}

function getRank(role) {
  if (role === 'user') return RANKS.commandant
  if (role === 'system') return null
  return RANKS.general
}

function RankIcon({ rank }) {
  if (rank === RANKS.commandant) {
    return (
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={rank.color} strokeWidth="2">
        <polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26"/>
      </svg>
    )
  }
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={rank.color} strokeWidth="2">
      <path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
    </svg>
  )
}

function ThinkingBlock({ content, done, raw }) {
  return (
    <div className={`feed-thinking-block ${done ? 'done' : 'active'}`}>
      <div className="feed-thinking-header">
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
          <circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
        </svg>
        <span>Reflexion</span>
        {!done && <span className="feed-thinking-dots"><span/><span/><span/></span>}
      </div>
      <div className="feed-thinking-content">
        {raw ? <span dangerouslySetInnerHTML={{ __html: content }} /> : content}
      </div>
    </div>
  )
}

const TOOL_ICONS = {
  terminal: '⌨',
  crush_run: '⚡',
  read_file: '📄',
  list_files: '📁',
  search_files: '🔍',
  grep_content: '🔎',
  get_config: '⚙',
  set_provider: '🔑',
  manage_ssh: '🌐',
  web_fetch: '🌐',
}

const TOOL_LABELS = {
  terminal: 'Terminal',
  crush_run: 'Crush Agent',
  read_file: 'Read File',
  list_files: 'List Files',
  search_files: 'Search Files',
  grep_content: 'Grep',
  get_config: 'Config',
  set_provider: 'Set Provider',
  manage_ssh: 'SSH',
  web_fetch: 'Web Fetch',
}

function ToolCallBlock({ call, result, activeAgents, onModeChange }) {
  const icon = TOOL_ICONS[call.name] || '🔧'
  const label = TOOL_LABELS[call.name] || call.name
  const isErr = result && result.is_error
  const isCrush = call.name === 'crush_run'
  const isClaude = call.name === 'claude_run'
  const isAgent = isCrush || isClaude
  const agentType = isCrush ? 'crush' : isClaude ? 'claude' : null
  const maxAgents = isCrush ? 2 : isClaude ? 2 : 0
  const currentCount = agentType && activeAgents ? (activeAgents[agentType] || 0) : 0
  const [mode, setMode] = useState('sync')

  let argsPreview = ''
  try {
    const args = typeof call.args === 'string' ? JSON.parse(call.args) : call.args
    if (args.command) argsPreview = args.command
    else if (args.task) argsPreview = args.task
    else if (args.path) argsPreview = args.path
    else if (args.pattern) argsPreview = args.pattern
    else if (args.url) argsPreview = args.url
    else if (args.action) argsPreview = args.action
    else argsPreview = JSON.stringify(args).slice(0, 80)
  } catch {
    argsPreview = String(call.args).slice(0, 80)
  }

  const truncatedResult = result ? (result.content || '').slice(0, 2000) : null

  const handleModeChange = (newMode) => {
    setMode(newMode)
    if (onModeChange) onModeChange(call.tool_call_id, newMode)
  }

  return (
    <div className={`studio-tool-block ${isErr ? 'error' : ''} ${result ? 'done' : 'running'}`}>
      <div className="studio-tool-header">
        <span className="studio-tool-icon">{icon}</span>
        <span className="studio-tool-name">{label}</span>
        {isAgent && !result && (
          <span className="studio-agent-badge">{currentCount}/{maxAgents}</span>
        )}
        {!result && <span className="studio-tool-spinner"><span/><span/><span/></span>}
        {result && <span className={`studio-tool-status ${isErr ? 'error' : 'ok'}`}>{isErr ? '✗' : '✓'}</span>}
      </div>
      <div className="studio-tool-args" title={argsPreview}>{argsPreview}</div>
      {isAgent && !result && (
        <div className="studio-agent-mode">
          <button
            className={`studio-mode-btn ${mode === 'sync' ? 'active' : ''}`}
            onClick={() => handleModeChange('sync')}
          >
            Exécuter et attendre
          </button>
          <button
            className={`studio-mode-btn ${mode === 'async' ? 'active' : ''}`}
            onClick={() => handleModeChange('async')}
          >
            Exécuter en arrière-plan
          </button>
        </div>
      )}
      {truncatedResult && (
        <div className="studio-tool-result">
          <pre>{truncatedResult}</pre>
        </div>
      )}
    </div>
  )
}

function MarkdownContent({ content, raw }) {
  if (raw) {
    return <pre className="feed-content" style={{ whiteSpace: 'pre-wrap', fontFamily: 'var(--font-mono)', fontSize: '0.9em' }}>{content}</pre>
  }
  return (
    <div className="feed-content">
      <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
    </div>
  )
}

function FeedItem({ msg, activeAgents, onModeChange }) {
  const isUser = msg.role === 'user'
  const isSystem = msg.role === 'system'
  const rank = getRank(msg.role)
  const [copiedMsg, setCopiedMsg] = useState(false)

  const timeStr = msg.time ? new Date(msg.time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : ''

  const renderMarkdown = useCallback((content) => {
    return <MarkdownContent content={content} raw={false} />
  }, [])
  let parsedToolCalls = null
  let parsedToolResults = null
  let parsedSegments = null
  let displayContent = msg.content
  try {
    const parsed = JSON.parse(msg.content)
    if (parsed && Array.isArray(parsed.segments)) {
      parsedSegments = parsed.segments
      parsedToolCalls = parsed.tool_calls || null
      parsedToolResults = parsed.tool_results || null
      displayContent = parsed.content || ''
    } else if (parsed && Array.isArray(parsed.tool_calls)) {
      parsedToolCalls = parsed.tool_calls
      parsedToolResults = parsed.tool_results || null
      displayContent = parsed.content || ''
    }
  } catch {}

  if (isSystem) {
    return (
      <div className="feed-item system">
        <div className="feed-system-badge" />
        <div className="feed-system-text">{msg.content}</div>
        {timeStr && <span className="feed-time">{timeStr}</span>}
      </div>
    )
  }

  let cleanContent = displayContent.replace(/<think[^>]*>[\s\S]*?<\/think>/gi, '')

  return (
    <div className={`feed-item ${msg.role}`}>
      <div className={`feed-avatar ${isUser ? 'user-rank' : 'ai-rank'}`}>
        <RankIcon rank={rank} />
      </div>
      <div className="feed-body">
        <div className="feed-header">
          <span className="feed-rank-badge" style={{ color: rank.color, borderColor: rank.color }}>
            {rank.short}
          </span>
          <span className="feed-role">{rank.label}</span>
          {timeStr && <span className="feed-time">{timeStr}</span>}
          {!isUser && !isSystem && (
            <button
              className="studio-copy-btn"
              onClick={() => {
                navigator.clipboard.writeText(displayContent)
                setCopiedMsg(true)
                setTimeout(() => setCopiedMsg(false), 1500)
              }}
              style={{ marginLeft: 'auto', fontSize: '0.7em', opacity: copiedMsg ? 1 : 0.5, transition: 'opacity 0.15s' }}
            >
              {copiedMsg ? '✓' : 'Copy MD'}
            </button>
          )}
        </div>
        {msg.thinking && <ThinkingBlock content={msg.thinking} done raw />}
        {msg.images && msg.images.length > 0 && (
          <div className="feed-images">
            {msg.images.map((imgId, i) => (
              <img key={i} className="feed-image" src={`/api/images/${imgId}`} alt={`Image ${i + 1}`} />
            ))}
          </div>
        )}
        {parsedSegments && parsedSegments.some(s => s.type === 'tool') ? (
          (() => {
            return (
              <>
                {parsedSegments.map((seg, i) => {
                  if (seg.type === 'text') {
                    if (!seg.content) return null
                    const c = seg.content.replace(/<think[^>]*>[\s\S]*?<\/think>/gi, '')
                    if (!c) return null
                    return (
                      <div key={`t${i}`} className="feed-content">
                        {renderMarkdown(c)}
                      </div>
                    )
                  }
                  if (seg.type === 'tool') {
                    const r = seg.result
                    const result = r && (r.content !== undefined || r.is_error !== undefined)
                      ? { content: r.content, is_error: r.is_error }
                      : null
                    return <ToolCallBlock key={`tc${i}`} call={seg.call} result={result} activeAgents={activeAgents} onModeChange={onModeChange} />
                  }
                  return null
                })}
              </>
            )
          })()
        ) : (
          <>
            {parsedToolCalls && (() => {
              return (
                <>
                  {parsedToolCalls.map((tc, i) => {
                    const resultData = parsedToolResults
                      ? parsedToolResults.find(r => r.tool_call_id === tc.tool_call_id)
                      : null
                    const result = resultData
                      ? { content: resultData.result, is_error: resultData.is_error }
                      : null
                    return <ToolCallBlock key={tc.tool_call_id || i} call={tc} result={result} activeAgents={activeAgents} onModeChange={onModeChange} />
                  })}
                </>
              )
            })()}
            {cleanContent && (
              <div className="feed-content">
                {renderMarkdown(cleanContent)}
              </div>
            )}
          </>
        )}
      </div>
    </div>
  )
}

function StreamingItem({ content, thinking, toolCalls, segments, activeAgents, onModeChange }) {
  const rank = RANKS.general
  const cleanContent = content.replace(/<think[^>]*>[\s\S]*?<\/think>/gi, '')
  const hasToolCalls = toolCalls && toolCalls.length > 0

  const hasOrderedSegments = segments && segments.some(s => s.type === 'tool')

  return (
    <div className="feed-item assistant">
      <div className="feed-avatar ai-rank">
        <RankIcon rank={rank} />
      </div>
      <div className="feed-body">
        <div className="feed-header">
          <span className="feed-rank-badge" style={{ color: rank.color, borderColor: rank.color }}>
            {rank.short}
          </span>
          <span className="feed-role">{rank.label}</span>
        </div>
        {thinking && <ThinkingBlock content={thinking} raw done={false} />}
        {hasOrderedSegments ? (
          <>
            {segments.map((seg, i) => {
              if (seg.type === 'text') {
                if (!seg.content) return null
                return (
                  <div key={`t${i}`} className="feed-content">
                    <MarkdownContent content={seg.content} raw={false} />
                  </div>
                )
              }
              if (seg.type === 'tool') {
                return <ToolCallBlock key={`tc${i}`} call={seg.call} result={seg.result} activeAgents={activeAgents} onModeChange={onModeChange} />
              }
              return null
            })}
          </>
        ) : (
          <>
            {hasToolCalls && toolCalls.map((tc, i) => (
                <ToolCallBlock key={tc.call?.tool_call_id || i} call={tc.call} result={tc.result} activeAgents={activeAgents} onModeChange={onModeChange} />
              ))
            }
            {cleanContent && (
              <div className="feed-content">
                <MarkdownContent content={cleanContent} raw={false} />
                <span className="studio-cursor" />
              </div>
            )}
          </>
        )}
        {!thinking && !cleanContent && !hasToolCalls && !hasOrderedSegments && (
          <div className="feed-content">
            <div className="studio-thinking"><span /><span /><span /></div>
          </div>
        )}
        {!hasOrderedSegments && cleanContent && (
          <span className="studio-cursor" />
        )}
      </div>
    </div>
  )
}

export default function Studio({ api }) {
  const { t } = useI18n()
  const [messages, setMessages] = useState([])
  const [input, setInput] = useState('')
  const [loading, setLoading] = useState(false)
  const [streaming, setStreaming] = useState('')
  const [streamThinking, setStreamThinking] = useState('')
  const [streamToolCalls, setStreamToolCalls] = useState([])
  const [streamSegments, setStreamSegments] = useState(null)
  const [loaded, setLoaded] = useState(false)
  const [tokenInfo, setTokenInfo] = useState({ used: 0, max: 150000, summarizeAt: 120000 })
  const [contextCollapsed, setContextCollapsed] = useState(false)
  const [messagesCollapsed, setMessagesCollapsed] = useState(false)
  const [sudoModal, setSudoModal] = useState(null)
  const [attachedImages, setAttachedImages] = useState([])
  const [activeAgents, setActiveAgents] = useState({ crush: 0, claude: 0 })
  const [advancedReflection, setAdvancedReflection] = useState(() => {
    try { return localStorage.getItem('muyue.advancedReflection') === 'true' } catch { return false }
  })
  const MAX_CRUSH_AGENTS = 2
  const MAX_CLAUDE_AGENTS = 2
  const messagesEnd = useRef(null)
  const feedRef = useRef(null)
  const textareaRef = useRef(null)
  const abortRef = useRef(null)
  const fileInputRef = useRef(null)
  const ragFileRef = useRef(null)
  const [ragStatus, setRagStatus] = useState(null)

  useEffect(() => {
    api.getChatHistory().then(data => {
      if (data.messages && data.messages.length > 0) {
        setMessages(data.messages)
      } else {
        setMessages([
          { id: 'welcome', role: 'system', content: t('studio.welcomeNew'), time: new Date().toISOString() },
        ])
      }
      setTokenInfo({
        used: data.tokens || 0,
        max: data.max_tokens || 150000,
        summarizeAt: data.summarize_at || 120000,
      })
      setLoaded(true)
    }).catch(() => {
      setMessages([
        { id: 'welcome', role: 'system', content: t('studio.welcomeNew'), time: new Date().toISOString() },
      ])
      setLoaded(true)
    })
  }, [])

  useEffect(() => {
    messagesEnd.current?.scrollIntoView({ behavior: 'smooth' })
  }, [messages, streaming, streamThinking, streamToolCalls])

  useEffect(() => {
    api.ragStatus().then(setRagStatus).catch(() => {})
  }, [api])

  useEffect(() => {
    const onTab = (e) => {
      if (e.key !== 'Tab') return
      if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') return
      const feed = document.querySelector('.studio-feed-layout')
      if (!feed?.closest('.tab-hidden')) {
        e.preventDefault()
        textareaRef.current?.focus()
      }
    }
    window.addEventListener('keydown', onTab)
    return () => window.removeEventListener('keydown', onTab)
  }, [])

  useEffect(() => {
    if (textareaRef.current) {
      textareaRef.current.style.height = 'auto'
      textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 200) + 'px'
    }
  }, [input])

  const refreshTokens = useCallback(async () => {
    try {
      const data = await api.getChatHistory()
      setTokenInfo({
        used: data.tokens || 0,
        max: data.max_tokens || 150000,
        summarizeAt: data.summarize_at || 120000,
      })
    } catch {}
  }, [api])

  const handleSummarize = useCallback(async () => {
    setMessages(prev => [...prev, { id: Date.now().toString(), role: 'system', content: 'Résumé de la conversation en cours...', time: new Date().toISOString() }])
    setContextCollapsed('animating')
    try {
      const data = await api.summarizeChat()
      setTokenInfo(prev => ({ ...prev, used: data.tokens || 0 }))
      setTimeout(() => {
        setMessages(prev => [...prev, { id: (Date.now() + 1).toString(), role: 'system', content: '✓ Conversation résumée automatiquement. Le contexte a été compressé.', time: new Date().toISOString(), compressed: true }])
        setContextCollapsed(true)
        setMessagesCollapsed(true)
      }, 600)
    } catch (err) {
      setMessages(prev => [...prev, { id: (Date.now() + 1).toString(), role: 'system', content: `Erreur de résumé: ${err.message}`, time: new Date().toISOString() }])
      setContextCollapsed(false)
    }
  }, [api])

  const handleClear = useCallback(async () => {
    try {
      await api.clearChat()
      setMessages([
        { id: 'clear-' + Date.now(), role: 'system', content: t('studio.cleared'), time: new Date().toISOString() },
      ])
    } catch {}
  }, [api, t])

  const handleImageSelect = useCallback((e) => {
    const files = Array.from(e.target.files || [])
    if (files.length === 0) return
    const remaining = 3 - attachedImages.length
    const toProcess = files.slice(0, remaining)
    toProcess.forEach(file => {
      if (!file.type.match(/^image\/(jpeg|jpg|png|webp)$/)) return
      if (file.size > 50 * 1024 * 1024) return
      const reader = new FileReader()
      reader.onload = (ev) => {
        setAttachedImages(prev => {
          if (prev.length >= 3) return prev
          return [...prev, { data: ev.target.result, filename: file.name, mime_type: file.type }]
        })
      }
      reader.readAsDataURL(file)
    })
    e.target.value = ''
  }, [attachedImages.length])

  const removeImage = useCallback((index) => {
    setAttachedImages(prev => prev.filter((_, i) => i !== index))
  }, [])

  const handleRAGFileSelect = useCallback(async (e) => {
    const files = Array.from(e.target.files || [])
    if (files.length === 0) return
    for (const file of files) {
      try {
        await api.ragIndexFile(file)
      } catch (err) {
        setMessages(prev => [...prev, { id: Date.now().toString(), role: 'system', content: `RAG: erreur d'indexation de ${file.name}: ${err.message}`, time: new Date().toISOString() }])
      }
    }
    api.ragStatus().then(setRagStatus).catch(() => {})
    e.target.value = ''
  }, [api])

  const handleSend = useCallback(async () => {
    if (!input.trim() || loading) return
    const text = input.trim()
    const images = [...attachedImages]
    setInput('')
    setAttachedImages([])

    const isSlashCommand = (t) => /^\/(clear|help|summarize|model(?:\s+\S+)?)$/.test(t)

    if (text.startsWith('/') && !isSlashCommand(text)) {
      setMessages(prev => [...prev, { id: Date.now().toString(), role: 'user', content: text, time: new Date().toISOString() }])
      setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'Commande inconnue. Tapez `/help` pour la liste des commandes.', time: new Date().toISOString() }])
      return
    }

    if (text === '/clear') {
      handleClear()
      return
    }

    if (text === '/help') {
      const helpMsg = [
        '## Commandes Studio',
        '',
        '- `/clear` - Effacer la conversation',
        '- `/summarize` - Résumer la conversation précédente',
        '- `/help` - Afficher cette aide',
        '- `/model` - Afficher le provider et modèle actifs',
        '- `/model change` - Basculer entre MiniMax et MiMo',
      ].join('\n')
      setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: helpMsg, time: new Date().toISOString() }])
      return
    }

    if (text === '/summarize') {
      handleSummarize()
      return
    }

    if (text === '/model' || text === '/model change') {
      if (text === '/model change') {
        api.getProviders().then(data => {
          const providers = data.providers || []
          const minimax = providers.find(p => p.name.toUpperCase() === 'MINIMAX')
          const mimo = providers.find(p => p.name.toUpperCase() === 'MIMO')
          if (!minimax || !mimo) {
            setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'MiniMax et MiMo doivent être configurés pour utiliser `/model change`.', time: new Date().toISOString() }])
            return
          }
          const active = providers.find(p => p.active)
          const activeName = active ? active.name.toUpperCase() : ''
          const switchTo = activeName === 'MINIMAX' ? 'MIMO' : 'MINIMAX'
          const target = switchTo === 'MINIMAX' ? minimax : mimo
          api.saveProvider({ name: target.name, active: true }).then(() => {
            setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: `✓ Provider changé: **${target.name}** (${target.model})`, time: new Date().toISOString() }])
          }).catch(() => {
            setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'Erreur lors du changement de provider.', time: new Date().toISOString() }])
          })
        }).catch(() => {
          setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'Erreur: impossible de récupérer les providers', time: new Date().toISOString() }])
        })
      } else {
        api.getProviders().then(data => {
          const active = data.providers?.find(p => p.active)
          const modelMsg = active ? `**${active.name}** — ${active.model}` : 'Aucun provider actif configuré'
          setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: modelMsg, time: new Date().toISOString() }])
        }).catch(() => {
          setMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: 'Erreur: impossible de récupérer les providers', time: new Date().toISOString() }])
        })
      }
      return
    }

    const userMsg = { id: Date.now().toString(), role: 'user', content: text, time: new Date().toISOString() }
    setMessages(prev => [...prev, userMsg])
    setLoading(true)
    setStreaming('')
    setStreamThinking('')
    setStreamToolCalls([])

    const controller = new AbortController()
    abortRef.current = controller

    try {
      let segments = []
      let textStartIdx = 0
      let thinking = ''

      const _updateLastText = (text) => {
        if (!text) return
        const last = segments.length > 0 ? segments[segments.length - 1] : null
        if (last && last.type === 'text') {
          last.content = text
        } else {
          segments.push({ type: 'text', content: text })
        }
      }

      await api.sendChat(text, true, (partial, event) => {
        if (event && (event.thinking_start || event.thinking_end || event.thinking !== undefined)) {
          if (event.thinking !== undefined) {
            thinking += event.thinking
            setStreamThinking(thinking)
          }
          return
        }
        if (event && event.tool_call) {
          _updateLastText(partial.slice(textStartIdx))
          textStartIdx = partial.length
          segments.push({ type: 'tool', call: event.tool_call, result: null })
          const toolName = event.tool_call.name
          if (toolName === 'crush_run' || toolName === 'claude_run') {
            const agentType = toolName === 'crush_run' ? 'crush' : 'claude'
            setActiveAgents(prev => ({ ...prev, [agentType]: prev[agentType] + 1 }))
          }
          const snap = segments.map(s => ({ ...s }))
          setStreamToolCalls(snap.filter(s => s.type === 'tool'))
          setStreamSegments(snap)
          setStreaming(partial)
          return
        }
        if (event && event.tool_result) {
          if (event.tool_result.sudo_blocked) {
            setSudoModal({ command: event.tool_result.command || event.tool_result.content })
          }
          const segIdx = segments.findIndex(s => s.type === 'tool' && s.call && s.call.tool_call_id === event.tool_result.tool_call_id)
          if (segIdx >= 0) {
            segments[segIdx].result = event.tool_result
            const toolName = segments[segIdx].call?.name
            if (toolName === 'crush_run' || toolName === 'claude_run') {
              const agentType = toolName === 'crush_run' ? 'crush' : 'claude'
              setActiveAgents(prev => ({ ...prev, [agentType]: Math.max(0, prev[agentType] - 1) }))
            }
            const snap = segments.map(s => ({ ...s }))
            setStreamToolCalls(snap.filter(s => s.type === 'tool'))
            setStreamSegments(snap)
          }
          return
        }
        _updateLastText(partial.slice(textStartIdx))
        setStreaming(partial)
        const snap = segments.map(s => ({ ...s }))
        setStreamSegments(snap)
      }, controller.signal, images, advancedReflection)

      const allText = segments.filter(s => s.type === 'text').map(s => s.content).join('')
      const toolSegs = segments.filter(s => s.type === 'tool')
      const finalContent = allText || t('studio.noResponse')
      const aiMsg = {
        id: (Date.now() + 1).toString(),
        role: 'assistant',
        content: finalContent,
        time: new Date().toISOString(),
      }
      if (thinking) aiMsg.thinking = thinking
      if (toolSegs.length > 0 || segments.length > 1) {
        aiMsg.content = JSON.stringify({
          segments: segments.map(s => s.type === 'text'
            ? { type: 'text', content: s.content }
            : { type: 'tool', call: s.call, result: { content: s.result?.content || '', is_error: s.result?.is_error || false, tool_call_id: s.call?.tool_call_id } }
          ),
          content: allText,
          tool_calls: toolSegs.map(s => s.call),
          tool_results: toolSegs.map(s => ({
            tool_call_id: s.call?.tool_call_id,
            result: s.result?.content || '',
            is_error: s.result?.is_error || false,
          })),
        })
      }
      setMessages(prev => [...prev, aiMsg])
    } catch (err) {
      if (err.name === 'AbortError') {
        if (streaming) {
          setMessages(prev => [...prev, {
            id: (Date.now() + 1).toString(),
            role: 'system',
            content: t('studio.cancelled'),
            time: new Date().toISOString(),
          }])
        }
      } else {
        setMessages(prev => [...prev, {
          id: (Date.now() + 1).toString(),
          role: 'system',
          content: `${t('studio.error')}: ${err.message}`,
          time: new Date().toISOString(),
        }])
      }
    } finally {
      setLoading(false)
      setStreaming('')
      setStreamThinking('')
      setStreamToolCalls([])
      setStreamSegments(null)
      setActiveAgents({ crush: 0, claude: 0 })
      setToolModes({})
      abortRef.current = null
      refreshTokens()
    }
  }, [input, loading, api, t, handleClear, streaming, refreshTokens, handleSummarize, attachedImages])

  const handleStop = useCallback(() => {
    if (abortRef.current) {
      abortRef.current.abort()
    }
  }, [])

  const handleToolModeChange = useCallback((toolCallId, mode) => {
    setToolModes(prev => ({ ...prev, [toolCallId]: mode }))
  }, [])

  const COMMANDS = ['/clear', '/summarize', '/help', '/model', '/model change']

  const handleKeyDown = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault()
      handleSend()
      return
    }
    if (e.key === 'Tab') {
      e.preventDefault()
      const ta = textareaRef.current
      if (!ta) return
      if (document.activeElement !== ta) {
        ta.focus()
        return
      }
      const val = ta.value
      const pos = ta.selectionStart
      const before = val.slice(0, pos)
      const afterSlash = before.match(/\/[\w ]*$/)
      if (afterSlash) {
        const partial = afterSlash[0]
        const matches = COMMANDS.filter(c => c.startsWith(partial) && c !== partial)
        if (matches.length >= 1) {
          let completed = matches[0]
          for (const m of matches) {
            while (!m.startsWith(completed)) completed = completed.slice(0, -1)
          }
          if (completed === partial && matches.length === 1) completed = matches[0]
          if (completed.length > partial.length) {
            const suffix = completed[completed.length - 1] === ' ' ? '' : (matches.length === 1 ? ' ' : '')
            completed += suffix
            const newText = val.slice(0, pos - afterSlash[0].length) + completed + val.slice(pos)
            setInput(newText)
            requestAnimationFrame(() => {
              ta.selectionStart = ta.selectionEnd = pos - afterSlash[0].length + completed.length
            })
          }
        }
      }
    }
  }

  const [summarizedExpanded, setSummarizedExpanded] = useState(false)

  const handleToggleCollapsed = useCallback(() => {
    setMessagesCollapsed(prev => !prev)
  }, [])

  const renderMessages = () => {
    const summarizedMsgs = messages.filter(m => m.summarized)
    const activeMsgs = messages.filter(m => !m.summarized)

    const renderSummaryBlock = () => summarizedMsgs.length > 0 && (
      <div className="feed-summary-block">
        <div className="feed-summary-header" onClick={() => setSummarizedExpanded(prev => !prev)}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/>
            <polyline points="14 2 14 8 20 8"/>
            <line x1="16" y1="13" x2="8" y2="13"/>
            <line x1="16" y1="17" x2="8" y2="17"/>
          </svg>
          <span className="feed-summary-text">Résumé · {summarizedMsgs.length} messages</span>
          <span className="feed-summary-toggle">{summarizedExpanded ? 'masquer' : 'voir'}</span>
        </div>
        {summarizedExpanded && summarizedMsgs.map(msg => (
          <FeedItem key={msg.id} msg={msg} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
        ))}
      </div>
    )

    if (messagesCollapsed && activeMsgs.length > 4) {
      const visibleCount = 4
      const hiddenCount = activeMsgs.length - visibleCount
      return (
        <>
          {renderSummaryBlock()}
          {activeMsgs.slice(0, visibleCount).map(msg => (
            <FeedItem key={msg.id} msg={msg} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
          ))}
          <div className="feed-collapsed-messages" onClick={handleToggleCollapsed}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
            </svg>
            <span className="feed-collapsed-text">{hiddenCount} messages antérieurs compressés</span>
            <span className="feed-collapsed-count">clic pour développer</span>
          </div>
        </>
      )
    }

    return (
      <>
        {renderSummaryBlock()}
        {activeMsgs.map(msg => (
          <FeedItem key={msg.id} msg={msg} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
        ))}
      </>
    )
  }

  if (!loaded) {
    return (
      <div className="studio-feed-layout">
        <div className="studio-feed">
          <div className="feed-loading">
            <div className="studio-thinking"><span /><span /><span /></div>
          </div>
        </div>
      </div>
    )
  }

  return (
    <div className="studio-feed-layout">
      <div className="studio-feed-scroll-wrap">
        <div className="studio-feed" ref={feedRef}>
          {renderMessages()}
          {(streaming || streamThinking || loading || streamToolCalls.length > 0) && (
            <StreamingItem content={streaming} thinking={streamThinking} toolCalls={streamToolCalls} segments={streamSegments} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
          )}
          <div ref={messagesEnd} style={{ height: '24px' }} />
        </div>
        <div className="studio-scroll-btns">
          <button className="studio-scroll-btn" onClick={() => feedRef.current?.scrollTo({ top: 0, behavior: 'smooth' })} title="Remonter">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M18 15l-6-6-6 6"/></svg>
          </button>
          <button className="studio-scroll-btn" onClick={() => messagesEnd.current?.scrollIntoView({ behavior: 'smooth' })} title="Descendre">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M6 9l6 6 6-6"/></svg>
          </button>
        </div>
      </div>

      <div className="studio-input-area">
        {attachedImages.length > 0 && (
          <div className="studio-image-previews">
            {attachedImages.map((img, i) => (
              <div key={i} className="studio-image-preview">
                <img src={img.data} alt={img.filename} />
                <button className="studio-image-remove" onClick={() => removeImage(i)}>×</button>
              </div>
            ))}
          </div>
        )}
        <div className={`studio-token-bar ${contextCollapsed === true ? 'compressed' : ''}`}>
          <div className={`studio-token-track ${contextCollapsed === true ? 'compressed' : ''}`}>
            <div
              className={`studio-token-fill ${tokenInfo.used >= tokenInfo.summarizeAt ? 'warn' : ''} ${contextCollapsed === true ? 'compressed' : ''} ${contextCollapsed === 'animating' ? 'animating' : ''}`}
              style={{ width: `${Math.min(100, (tokenInfo.used / tokenInfo.max) * 100)}%` }}
            />
          </div>
          <span className={`studio-token-text ${contextCollapsed === true ? 'compressed' : ''}`}>
            {(tokenInfo.used / 1000).toFixed(1)}k / {(tokenInfo.max / 1000).toFixed(0)}k tokens
            {contextCollapsed === true && ' · compressé'}
            {tokenInfo.used >= tokenInfo.summarizeAt && contextCollapsed !== true && ' · résumé auto.'}
          </span>
          {contextCollapsed === true && (
            <button className="ghost sm" onClick={handleToggleCollapsed} style={{ marginLeft: '8px', fontSize: '10px' }}>
              voir plus
            </button>
          )}
        </div>
        <div className="studio-input-row">
          <input
            type="file"
            ref={fileInputRef}
            accept="image/jpeg,image/png,image/webp"
            multiple
            style={{ display: 'none' }}
            onChange={handleImageSelect}
          />
          <input
            type="file"
            ref={ragFileRef}
            accept=".txt,.md,.go,.js,.ts,.py,.java,.rs,.jsx,.tsx,.json,.yaml,.yml,.csv,.html,.css,.sh,.bash,.zsh,.fish"
            multiple
            style={{ display: 'none' }}
            onChange={handleRAGFileSelect}
          />
          <button
            className="studio-attach-btn"
            onClick={() => fileInputRef.current?.click()}
            disabled={loading || attachedImages.length >= 3}
            title="Joindre des images (max 3)"
          >
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/>
            </svg>
          </button>
          <button
            className="studio-attach-btn"
            onClick={() => ragFileRef.current?.click()}
            disabled={loading}
            title={ragStatus ? `RAG: ${ragStatus.documents || 0} docs, ${ragStatus.chunks || 0} chunks` : 'Ajouter un contexte RAG'}
            style={ragStatus && ragStatus.documents > 0 ? { color: 'var(--accent, #6c5ce7)', borderColor: 'var(--accent, #6c5ce7)' } : undefined}
          >
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/>
            </svg>
          </button>
          <button
            className="studio-attach-btn"
            onClick={() => {
              const next = !advancedReflection
              setAdvancedReflection(next)
              try { localStorage.setItem('muyue.advancedReflection', String(next)) } catch {}
            }}
            disabled={loading}
            title={advancedReflection ? "Réflexion avancée: ON (un autre modèle produit un rapport préalable)" : "Réflexion avancée: OFF"}
            style={advancedReflection ? { color: 'var(--accent, #6c5ce7)', borderColor: 'var(--accent, #6c5ce7)' } : undefined}
          >
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
            </svg>
          </button>
          <textarea
            ref={textareaRef}
            value={input}
            onChange={e => setInput(e.target.value)}
            onKeyDown={handleKeyDown}
            placeholder={t('studio.placeholderNew')}
            disabled={loading}
            rows={1}
          />
          <button
            className="studio-send-btn"
            onClick={handleSend}
            disabled={loading || !input.trim()}
          >
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
            </svg>
          </button>
          {loading && (
            <button className="studio-stop-btn" onClick={handleStop} title={t('studio.stop')}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                <rect x="4" y="4" width="16" height="16" rx="2"/>
              </svg>
            </button>
          )}
        </div>
        <div className="studio-input-hint">
          {t('studio.inputHint')} · /clear /summarize /help /model · @fichier.ext pour joindre un fichier{attachedImages.length > 0 && ` · ${attachedImages.length} image${attachedImages.length > 1 ? 's' : ''} attachée${attachedImages.length > 1 ? 's' : ''}`}
        </div>
      </div>

      {sudoModal && (
        <div className="shell-modal-overlay" onClick={() => setSudoModal(null)}>
          <div className="shell-modal" onClick={e => e.stopPropagation()}>
            <div className="shell-modal-header">Commande bloquée</div>
            <div className="shell-modal-body">
              <p style={{ color: 'var(--accent-bright)', fontWeight: 600, marginBottom: 8 }}>L'IA a tenté d'exécuter une commande nécessitant des privilèges administrateur :</p>
              <pre style={{ background: 'var(--bg)', padding: '10px 12px', borderRadius: 'var(--radius)', fontSize: 12, overflow: 'auto', fontFamily: 'var(--font-mono)' }}>{sudoModal.command}</pre>
              <p style={{ color: 'var(--text-secondary)', fontSize: 12, marginTop: 12 }}>La commande a été bloquée. L'IA en a été informée et cherchera une alternative.</p>
            </div>
            <div className="shell-modal-footer">
              <button className="primary" onClick={() => setSudoModal(null)}>Compris</button>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
