import { useState, useRef, useEffect, useCallback } from 'react'
import { Terminal as XTerm } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import { WebLinksAddon } from '@xterm/addon-web-links'
import { Plus, X, Monitor, Globe, ChevronDown, Pencil, Trash2, Search, Copy, Send, Eye } from 'lucide-react'
import '@xterm/xterm/css/xterm.css'
import { useI18n } from '../i18n'

const MAX_TABS = 7
const SHELL_MAX_TOKENS = 100000
const TABS_STORAGE_KEY = 'muyue_shell_tabs'
const TERMINAL_BUFFER_KEY = 'muyue_terminal_buffers'

function renderContent(text) {
  const parts = []
  const codeBlockRegex = /(```[\s\S]*?```)/g
  let match
  let lastIndex = 0
  while ((match = codeBlockRegex.exec(text)) !== null) {
    if (match.index > lastIndex) {
      parts.push({ type: 'text', content: text.slice(lastIndex, match.index) })
    }
    const full = match[1]
    const firstNewline = full.indexOf('\n')
    const lang = firstNewline > -1 ? full.slice(3, firstNewline).trim() : ''
    const code = firstNewline > -1 ? full.slice(firstNewline + 1, -3) : full.slice(3, -3)
    parts.push({ type: 'code', lang, content: code })
    lastIndex = match.index + full.length
  }
  if (lastIndex < text.length) {
    const remaining = text.slice(lastIndex)
    const openBlock = remaining.match(/```(\w*)\n?([\s\S]*)$/)
    if (openBlock) {
      if (openBlock.index > 0) {
        parts.push({ type: 'text', content: remaining.slice(0, openBlock.index) })
      }
      parts.push({ type: 'code', lang: openBlock[1] || '', content: openBlock[2] || '' })
    } else {
      parts.push({ type: 'text', content: remaining })
    }
  }
  return parts
}

function formatText(text) {
  let html = text
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')

  html = html
    .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
    .replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>')
    .replace(/^### (.+)$/gm, '<h4 class="msg-h4">$1</h4>')
    .replace(/^## (.+)$/gm, '<h3 class="msg-h3">$1</h3>')
    .replace(/^# (.+)$/gm, '<h2 class="msg-h2">$1</h2>')
    .replace(/^\s*[-*] (.+)$/gm, '<div class="msg-bullet">• $1</div>')
    .replace(/^\s*(\d+)[.)] (.+)$/gm, '<div class="msg-step"><span class="msg-step-num">$1</span> $2</div>')
    .replace(/\n/g, '<br/>')

  html = html
    .replace(/<br\/>\s*<br\/>/g, '<br/>')
    .replace(/<br\/>\s*(<h[234]|<div class="msg-)/g, '$1')
    .replace(/(<\/h[234]|<\/div>)\s*<br\/>/g, '$1')
    .replace(/\s+on\w+=["'][^"']*["']/gi, '')
    .replace(/javascript:/gi, '')
    .replace(/data:/gi, '')

  return html
}

const THEMES = {
  default: {
    background: '#0A0A0C', foreground: '#EAE0E2', cursor: '#FF0033',
    cursorAccent: '#0A0A0C', selectionBackground: '#FF003344', selectionForeground: '#ffffff',
    black: '#0A0A0C', red: '#FF0033', green: '#00E676', yellow: '#FFD740',
    blue: '#448AFF', magenta: '#FF1A5E', cyan: '#00BCD4', white: '#EAE0E2',
    brightBlack: '#5A4F52', brightRed: '#FF5252', brightGreen: '#69F0AE',
    brightYellow: '#FFFF00', brightBlue: '#82B1FF', brightMagenta: '#FF80AB',
    brightCyan: '#84FFFF', brightWhite: '#FFFFFF',
  },
  monokai: {
    background: '#272822', foreground: '#F8F8F2', cursor: '#F8F8F0',
    cursorAccent: '#272822', selectionBackground: '#F8F8F244', selectionForeground: '#ffffff',
    black: '#272822', red: '#F92672', green: '#A6E22E', yellow: '#E6DB74',
    blue: '#66D9EF', magenta: '#AE81FF', cyan: '#A1EFE4', white: '#F8F8F2',
    brightBlack: '#75715E', brightRed: '#F92672', brightGreen: '#A6E22E',
    brightYellow: '#E6DB74', brightBlue: '#66D9EF', brightMagenta: '#AE81FF',
    brightCyan: '#A1EFE4', brightWhite: '#F8F8F2',
  },
  gruvbox: {
    background: '#282828', foreground: '#EBDBB2', cursor: '#FB4934',
    cursorAccent: '#282828', selectionBackground: '#EBDBB244', selectionForeground: '#ffffff',
    black: '#282828', red: '#CC241D', green: '#98971A', yellow: '#D79921',
    blue: '#458588', magenta: '#B16286', cyan: '#689D6A', white: '#EBDBB2',
    brightBlack: '#928374', brightRed: '#FB4934', brightGreen: '#B8BB26',
    brightYellow: '#FABC2A', brightBlue: '#83A598', brightMagenta: '#D3869B',
    brightCyan: '#8EC07C', brightWhite: '#EBDBB2',
  },
  nord: {
    background: '#2E3440', foreground: '#D8DEE9', cursor: '#D8DEE9',
    cursorAccent: '#2E3440', selectionBackground: '#D8DEE944', selectionForeground: '#ffffff',
    black: '#2E3440', red: '#BF616A', green: '#A3BE8C', yellow: '#EBCB8B',
    blue: '#81A1C1', magenta: '#B48EAD', cyan: '#88C0D0', white: '#D8DEE9',
    brightBlack: '#4C566A', brightRed: '#BF616A', brightGreen: '#A3BE8C',
    brightYellow: '#EBCB8B', brightBlue: '#81A1C1', brightMagenta: '#B48EAD',
    brightCyan: '#8FBCBB', brightWhite: '#ECEFF4',
  },
  'solarized-dark': {
    background: '#002B36', foreground: '#839496', cursor: '#D33682',
    cursorAccent: '#002B36', selectionBackground: '#83949644', selectionForeground: '#ffffff',
    black: '#002B36', red: '#DC322F', green: '#859900', yellow: '#B58900',
    blue: '#268BD2', magenta: '#D33682', cyan: '#2AA198', white: '#FDF6E3',
    brightBlack: '#073642', brightRed: '#CB4B16', brightGreen: '#586E75',
    brightYellow: '#657B83', brightBlue: '#6C71C4', brightMagenta: '#6C71C4',
    brightCyan: '#93A1A1', brightWhite: '#FDF6E3',
  },
  dracula: {
    background: '#282A36', foreground: '#F8F8F2', cursor: '#F8F8F2',
    cursorAccent: '#282A36', selectionBackground: '#F8F8F244', selectionForeground: '#ffffff',
    black: '#282A36', red: '#FF5555', green: '#50FA7B', yellow: '#F1FA8C',
    blue: '#BD93F9', magenta: '#FF79C6', cyan: '#8BE9FD', white: '#F8F8F2',
    brightBlack: '#6272A4', brightRed: '#FF6E6E', brightGreen: '#69FF94',
    brightYellow: '#FFFFA5', brightBlue: '#D6ACFF', brightMagenta: '#FF92DF',
    brightCyan: '#A4FFFF', brightWhite: '#FFFFFF',
  },
}

function getTheme(themeName) {
  return THEMES[themeName] || THEMES.default
}

function createTerminal(container, settings = {}) {
  const theme = getTheme(settings.theme || 'default')
  const term = new XTerm({
    cursorBlink: true,
    fontSize: settings.fontSize || 12,
    fontFamily: settings.fontFamily || "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
    theme,
    allowTransparency: false,
    scrollback: 5000,
  })

  const fitAddon = new FitAddon()
  const webLinksAddon = new WebLinksAddon()
  term.loadAddon(fitAddon)
  term.loadAddon(webLinksAddon)
  term.open(container)
  fitAddon.fit()

  return { term, fitAddon }
}

function connectWebSocket(term, fitAddon, initPayload, onStateChange, onFirstMessage) {
  const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
  const ws = new WebSocket(`${proto}//${window.location.host}/api/ws/terminal`)

  ws.addEventListener('open', () => {
    ws.send(JSON.stringify(initPayload))
    const dims = fitAddon.proposeDimensions()
    if (dims) {
      ws.send(JSON.stringify({ type: 'resize', rows: dims.rows, cols: dims.cols }))
    }
    if (onStateChange) onStateChange(true)
  })

  let firstMessage = true
  ws.addEventListener('message', (event) => {
    if (firstMessage) {
      firstMessage = false
      if (onFirstMessage) onFirstMessage()
    }
    try {
      const msg = JSON.parse(event.data)
      if (msg.type === 'output') {
        term.write(msg.data)
      } else if (msg.type === 'error') {
        term.write(`\r\n\x1b[31mError: ${msg.data}\x1b[0m\r\n`)
      }
    } catch {
      term.write(event.data)
    }
  })

  ws.addEventListener('close', () => {
    term.write('\r\n\x1b[33m— Connection closed —\x1b[0m\r\n')
    if (onStateChange) onStateChange(false)
  })

  ws.addEventListener('error', () => {
    term.write('\r\n\x1b[31m— Connection error —\x1b[0m\r\n')
    if (onStateChange) onStateChange(false)
  })

  term.onResize(({ rows, cols }) => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: 'resize', rows, cols }))
    }
  })

  return ws
}

export default function Shell({ api }) {
  const { t } = useI18n()
  const tabsRef = useRef({})
  const nextIdRef = useRef(1)
  const settingsRef = useRef({ fontSize: 12, fontFamily: "'JetBrains Mono', 'Fira Code', monospace", theme: 'default' })
  const pendingCommandsRef = useRef({})

  const savedTabs = (() => {
    try {
      const raw = localStorage.getItem(TABS_STORAGE_KEY)
      if (raw) {
        const parsed = JSON.parse(raw)
        if (Array.isArray(parsed) && parsed.length > 0) {
          return parsed.map(t => ({ ...t, connected: false }))
        }
      }
    } catch {}
    return null
  })()

  const [tabs, setTabs] = useState(savedTabs || [
    { id: 1, name: 'Local Shell', type: 'local', shell: '', connected: false },
  ])
  const [activeTab, setActiveTab] = useState(() => {
    if (savedTabs) {
      return savedTabs[0]?.id || 1
    }
    return 1
  })
  const activeTabRef = useRef(activeTab)
  useEffect(() => { activeTabRef.current = activeTab }, [activeTab])
  const [sshConnections, setSshConnections] = useState([])
  const [systemTerminals, setSystemTerminals] = useState([])
  const [showMenu, setShowMenu] = useState(false)
  const [showSshModal, setShowSshModal] = useState(false)
  const [editingTab, setEditingTab] = useState(null)
  const [editName, setEditName] = useState('')
  const [terminalSettings, setTerminalSettings] = useState({
    fontSize: 12,
    fontFamily: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
    theme: 'default',
  })

  useEffect(() => { settingsRef.current = terminalSettings }, [terminalSettings])

  const [sshForm, setSshForm] = useState({
    name: '', host: '', port: 22, user: '', key_path: '',
  })

  const [aiMessages, setAiMessages] = useState([])
  const [aiInput, setAiInput] = useState('')
  const [aiLoading, setAiLoading] = useState(false)
  const [aiTokens, setAiTokens] = useState(0)
  const [aiAtLimit, setAiAtLimit] = useState(false)
  const [analyzing, setAnalyzing] = useState(false)
  const [showAnalysis, setShowAnalysis] = useState(false)
  const [analysisContent, setAnalysisContent] = useState('')
  const aiMessagesRef = useRef(null)
  const aiLoadedRef = useRef(false)
  const aiLoadingRef = useRef(false)

  useEffect(() => {
    aiMessagesRef.current?.scrollTo(0, aiMessagesRef.current.scrollHeight)
  }, [aiMessages])

  useEffect(() => {
    api.getShellAnalysis?.().then(d => {
      if (d?.analysis) setAnalysisContent(d.analysis)
    }).catch(() => {
      const stored = localStorage.getItem('shell_analysis')
      if (stored) setAnalysisContent(stored)
    })
  }, [])

  useEffect(() => {
    if (aiLoadedRef.current) return
    aiLoadedRef.current = true
    api.getShellChatHistory().then(d => {
      if (d.messages && d.messages.length > 0) {
        setAiMessages(d.messages)
      } else {
        setAiMessages([{ role: 'assistant', content: t('shell.aiWelcome') || 'Système Analyste prêt. Tapez /help pour les commandes.' }])
      }
      setAiTokens(d.tokens || 0)
      setAiAtLimit(d.at_limit || false)
    }).catch(() => {
      setAiMessages([{ role: 'assistant', content: 'Système Analyste prêt.' }])
    })
  }, [])

  useEffect(() => {
    const maxId = tabs.reduce((max, t) => Math.max(max, t.id), 0)
    nextIdRef.current = maxId + 1
  }, [])

  useEffect(() => {
    api.getTerminalSessions().then(d => {
      setSshConnections(d.ssh || [])
      setSystemTerminals(d.system || [])
    }).catch(() => {})
    api.getConfig().then(d => {
      if (d.terminal) {
        setTerminalSettings({
          fontSize: d.terminal.font_size || 12,
          fontFamily: d.terminal.font_family || "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
          theme: d.terminal.theme || 'default',
        })
      }
    }).catch(() => {})
  }, [])

  const initTerminal = useCallback((tabId, tab) => {
    if (tabsRef.current[tabId]) return

    const container = document.getElementById(`terminal-${tabId}`)
    if (!container) return

    const s = settingsRef.current
    const { term, fitAddon } = createTerminal(container, {
      fontSize: s.fontSize,
      fontFamily: s.fontFamily,
      theme: s.theme,
    })

    let initPayload
    if (tab.type === 'ssh') {
      initPayload = {
        type: 'ssh',
        data: JSON.stringify({
          host: tab.host,
          port: tab.port || 22,
          user: tab.user || 'root',
          key_path: tab.key_path || '',
        }),
      }
    } else {
      initPayload = {
        type: 'shell',
        data: tab.shell || '',
      }
    }

    let disposed = false

    const saveBuffer = () => {
      try {
        const buf = term.buffer.active
        const lines = []
        for (let i = 0; i < buf.length; i++) {
          const line = buf.getLine(i)
          if (line) lines.push(line.translateToString(true))
        }
        const savedBuffers = JSON.parse(sessionStorage.getItem(TERMINAL_BUFFER_KEY) || '{}')
        savedBuffers[tabId] = lines.join('\n')
        sessionStorage.setItem(TERMINAL_BUFFER_KEY, JSON.stringify(savedBuffers))
      } catch (e) { console.warn('[Shell] Buffer save failed:', e) }
    }

    const onWsState = (connected) => {
      if (disposed) return
      if (!connected) saveBuffer()
      setTabs(prev => prev.map(t => t.id === tabId ? { ...t, connected } : t))
    }

    const restoreBuffer = () => {
      try {
        const savedBuffers = JSON.parse(sessionStorage.getItem(TERMINAL_BUFFER_KEY) || '{}')
        if (savedBuffers[tabId]) {
          term.write('\x1b[90m— session restaurée —\x1b[0m\r\n')
          term.write(savedBuffers[tabId])
        }
      } catch (e) { console.warn('[Shell] Buffer restore failed:', e) }
    }

    const ws = connectWebSocket(term, fitAddon, initPayload, onWsState, restoreBuffer)

    const clearBufferOnClear = () => {
      try {
        const buf = term.buffer.active
        const lineY = buf.length - 1
        const line = buf.getLine(lineY)
        if (line) {
          const text = line.translateToString(true).trim().toLowerCase()
          if (text === 'clear' || text === '$ clear' || text.endsWith(' clear')) {
            const savedBuffers = JSON.parse(sessionStorage.getItem(TERMINAL_BUFFER_KEY) || '{}')
            delete savedBuffers[tabId]
            sessionStorage.setItem(TERMINAL_BUFFER_KEY, JSON.stringify(savedBuffers))
          }
        }
      } catch (e) { console.warn('[Shell] Clear detection failed:', e) }
    }

    term.onData((data) => {
      if (data === '\r') clearBufferOnClear()
      if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: 'input', data }))
      }
    })

    const onResize = () => {
      fitAddon.fit()
    }

    const resizeObserver = new ResizeObserver(onResize)
    resizeObserver.observe(container)
    window.addEventListener('resize', onResize)

    const bufferSaveInterval = setInterval(() => { if (!disposed) saveBuffer() }, 5000)

    console.log(`[Shell] initTerminal tab=${tabId} type=${tab.type} name="${tab.name}" shell="${tab.shell || '(default)'}"`)
    tabsRef.current[tabId] = { term, fitAddon, ws, resizeObserver, onResize, bufferSaveInterval, saveBuffer, disposed: () => disposed }
    tabsRef.current[tabId]._markDisposed = () => { disposed = true }
    console.log(`[Shell] initTerminal tab=${tabId} done, tabsRef keys:`, Object.keys(tabsRef.current))

    const pending = pendingCommandsRef.current[tabId]
    if (pending && pending.length > 0) {
      console.log(`[Shell] Flushing ${pending.length} pending commands for tab ${tabId}`)
      for (const cmd of pending) {
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify({ type: 'input', data: cmd + '\r' }))
        }
      }
      delete pendingCommandsRef.current[tabId]
    }
  }, [])

  const initPendingTabs = useCallback(() => {
    for (const tab of tabsRef.current._tabList || []) {
      if (!tabsRef.current[tab.id]) {
        const container = document.getElementById(`terminal-${tab.id}`)
        if (container && container.offsetHeight > 0) {
          initTerminal(tab.id, tab)
        }
      }
    }
    requestAnimationFrame(() => {
      for (const tab of tabsRef.current._tabList || []) {
        const entry = tabsRef.current[tab.id]
        if (entry) entry.fitAddon.fit()
      }
      setTimeout(() => {
        for (const tab of tabsRef.current._tabList || []) {
          const entry = tabsRef.current[tab.id]
          if (entry) entry.fitAddon.fit()
        }
      }, 150)
    })
  }, [initTerminal])

  useEffect(() => {
    tabsRef.current._tabList = tabs
  }, [tabs])

  useEffect(() => {
    let cancelled = false
    const pending = []

    const tryInitTab = (tab, attempt) => {
      if (cancelled) return
      const shellCol = document.querySelector('.shell-terminal-col')
      if (!shellCol || shellCol.offsetParent === null) {
        pending.push(setTimeout(() => tryInitTab(tab, attempt + 1), 200))
        return
      }
      const container = document.getElementById(`terminal-${tab.id}`)
      if (!container) {
        pending.push(setTimeout(() => tryInitTab(tab, attempt + 1), 100))
        return
      }
      if (container.offsetHeight === 0) {
        pending.push(setTimeout(() => tryInitTab(tab, attempt + 1), 100))
        return
      }
      if (!tabsRef.current[tab.id]) {
        initTerminal(tab.id, tab)
      }
      requestAnimationFrame(() => {
        if (cancelled) return
        const entry = tabsRef.current[tab.id]
        if (entry) {
          entry.fitAddon.fit()
          setTimeout(() => { if (!cancelled) entry.fitAddon.fit() }, 100)
        }
      })
      if (!tabsRef.current[tab.id]) {
        tryInitTab(tab, 0)
      }
    }

    const wrapper = document.querySelector('.shell-layout')?.parentElement
    let observer
    if (wrapper) {
      observer = new MutationObserver(() => {
        if (!wrapper.classList.contains('tab-hidden') && wrapper.offsetParent !== null) {
          initPendingTabs()
        }
      })
      observer.observe(wrapper, { attributes: true, attributeFilter: ['class'] })
    }

    return () => {
      cancelled = true
      pending.forEach(clearTimeout)
      observer?.disconnect()
    }
  }, [tabs, initTerminal, initPendingTabs])

  useEffect(() => {
    const entry = tabsRef.current[activeTab]
    if (entry) {
      requestAnimationFrame(() => {
        if (activeTabRef.current === activeTab) {
          entry.fitAddon.fit()
        }
      })
    }
  }, [activeTab])

  useEffect(() => {
    const iv = setInterval(() => {
      const wrapper = document.querySelector('.shell-layout')?.parentElement
      if (wrapper && wrapper.classList.contains('tab-hidden')) return
      const entry = tabsRef.current[activeTabRef.current]
      if (entry) {
        entry.fitAddon.fit()
      }
    }, 2000)
    return () => clearInterval(iv)
  }, [tabs])

  useEffect(() => {
    return () => {
      for (const [tabId, entry] of Object.entries(tabsRef.current)) {
        entry._markDisposed?.()
        if (entry.bufferSaveInterval) clearInterval(entry.bufferSaveInterval)
        window.removeEventListener('resize', entry.onResize)
        entry.resizeObserver?.disconnect()
        entry.ws?.close()
        entry.term?.dispose()
      }
      tabsRef.current = {}
    }
  }, [])

  useEffect(() => {
    const onKey = (e) => {
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return
      if (!e.altKey && !(e.key === 'Tab' && e.shiftKey)) return

      if (e.key === 'Tab' && e.shiftKey) {
        const shellTab = document.querySelector('.shell-layout')
        if (!shellTab || shellTab.closest('.tab-hidden')) return
        e.preventDefault()
        const idx = tabs.findIndex(t => t.id === activeTab)
        const next = (idx + 1) % tabs.length
        setActiveTab(tabs[next].id)
        return
      }

      const num = parseInt(e.key)
      if (num >= 1 && num <= tabs.length) {
        e.preventDefault()
        setActiveTab(tabs[num - 1].id)
      }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [tabs])

  const addLocalTab = (shell, name) => {
    if (tabs.length >= MAX_TABS) return
    const id = nextIdRef.current++
    const newTab = { id, name: name || `${t('shell.localShell')} ${tabs.length}`, type: 'local', shell: shell || '', connected: false }
    setTabs(prev => { const next = [...prev, newTab]; localStorage.setItem(TABS_STORAGE_KEY, JSON.stringify(next.map(t => ({ id: t.id, name: t.name, type: t.type, shell: t.shell, host: t.host, port: t.port, user: t.user, key_path: t.key_path })))); return next })
    setActiveTab(id)
    setShowMenu(false)
  }

  const addSSHTab = (conn) => {
    if (tabs.length >= MAX_TABS) return
    const id = nextIdRef.current++
    const newTab = {
      id,
      name: conn.name || `${conn.user}@${conn.host}`,
      type: 'ssh',
      host: conn.host,
      port: conn.port || 22,
      user: conn.user || 'root',
      key_path: conn.key_path || '',
      connected: false,
    }
    setTabs(prev => { const next = [...prev, newTab]; localStorage.setItem(TABS_STORAGE_KEY, JSON.stringify(next.map(t => ({ id: t.id, name: t.name, type: t.type, shell: t.shell, host: t.host, port: t.port, user: t.user, key_path: t.key_path })))); return next })
    setActiveTab(id)
    setShowMenu(false)
  }

  const closeTab = (tabId, e) => {
    if (e) e.stopPropagation()

    const entry = tabsRef.current[tabId]
    if (entry) {
      entry._markDisposed?.()
      entry.saveBuffer?.()
      if (entry.bufferSaveInterval) clearInterval(entry.bufferSaveInterval)
      window.removeEventListener('resize', entry.onResize)
      entry.resizeObserver.disconnect()
      entry.ws.close()
      entry.term.dispose()
      delete tabsRef.current[tabId]
    }

    try {
      const savedBuffers = JSON.parse(sessionStorage.getItem(TERMINAL_BUFFER_KEY) || '{}')
      delete savedBuffers[tabId]
      sessionStorage.setItem(TERMINAL_BUFFER_KEY, JSON.stringify(savedBuffers))
    } catch (e) { console.warn('[Shell] Buffer cleanup failed:', e) }

    setTabs(prev => {
      if (prev.length <= 1) return prev
      const next = prev.filter(t => t.id !== tabId)
      if (activeTab === tabId && next.length > 0) {
        setActiveTab(next[next.length - 1].id)
      }
      return next
    })
  }

  const startRename = (tabId, e) => {
    if (e) e.stopPropagation()
    const tab = tabs.find(t => t.id === tabId)
    setEditingTab(tabId)
    setEditName(tab.name)
  }

  const finishRename = () => {
    if (editName.trim() && editingTab) {
      setTabs(prev => prev.map(t => t.id === editingTab ? { ...t, name: editName.trim() } : t))
    }
    setEditingTab(null)
    setEditName('')
  }

  const saveSSHConnection = async () => {
    if (!sshForm.name.trim() || !sshForm.host.trim()) return
    try {
      await api.addSSHConnection(sshForm)
      setSshConnections(prev => [...prev, { ...sshForm }])
      setSshForm({ name: '', host: '', port: 22, user: '', key_path: '' })
      setShowSshModal(false)
    } catch (err) {
      console.error(err)
    }
  }

  const deleteSSHConnection = async (name) => {
    try {
      await api.deleteSSHConnection(name)
      setSshConnections(prev => prev.filter(c => c.name !== name))
    } catch (err) {
      console.error(err)
    }
  }

  const sendToTerminal = useCallback((code, tabId) => {
    const targetId = tabId || activeTabRef.current
    const entry = tabsRef.current[targetId]
    if (!entry) {
      console.warn(`[Shell] sendToTerminal: tab ${targetId} not ready. Queueing. tabsRef:`, Object.keys(tabsRef.current), 'activeTab:', activeTabRef.current, 'requested:', tabId)
      if (!pendingCommandsRef.current[targetId]) pendingCommandsRef.current[targetId] = []
      pendingCommandsRef.current[targetId].push(code)
      return
    }
    if (!entry.ws || entry.ws.readyState !== WebSocket.OPEN) {
      console.warn(`[Shell] sendToTerminal: WS not open for tab ${targetId} (state=${entry.ws?.readyState}). Queueing.`)
      if (!pendingCommandsRef.current[targetId]) pendingCommandsRef.current[targetId] = []
      pendingCommandsRef.current[targetId].push(code)
      return
    }
    console.log(`[Shell] sendToTerminal: tab ${targetId} ← ${code.length} chars`)
    entry.ws.send(JSON.stringify({ type: 'input', data: code + '\r' }))
  }, [])

  const focusAiTerminal = useCallback(() => {
    const entry = tabsRef.current[activeTabRef.current]
    if (entry) entry.term.focus()
  }, [])

  const _sendAiMessage = useCallback(async (text, fromEvent = false) => {
    if (!text || !text.trim() || aiLoadingRef.current || aiAtLimit) return
    const trimmed = text.trim()
    aiLoadingRef.current = true

    if (!fromEvent) {
      setAiInput('')
      focusAiTerminal()
    }

    if (trimmed === '/clear') {
      try {
        await api.clearShellChat()
        setAiMessages([{ role: 'assistant', content: t('shell.aiWelcome') || 'Contexte effacé. Prêt.' }])
        setAiTokens(0)
        setAiAtLimit(false)
      } catch {}
      aiLoadingRef.current = false
      return
    }

    if (trimmed === '/help') {
      setAiMessages(prev => [...prev,
        { role: 'user', content: trimmed },
        { role: 'assistant', content: 'Commandes disponibles:\n• /clear — Effacer la conversation\n• /help — Afficher l\'aide\n\nJe ne peux pas exécuter de code. Les blocs de code proposés peuvent être copiés ou envoyés directement au terminal actif.' }
      ])
      aiLoadingRef.current = false
      return
    }

    const currentTab = activeTabRef.current
    console.log(`[Shell] _sendAiMessage: activeTab=${currentTab}, fromEvent=${fromEvent}, text="${trimmed.slice(0, 50)}"`)
    setAiMessages(prev => [...prev, { role: 'user', content: trimmed, _tabId: currentTab }])
    setAiLoading(true)

    try {
      let accumulated = ''
      await api.sendShellChat(trimmed, {}, true, (partial) => {
        accumulated = partial
        setAiMessages(prev => {
          const filtered = prev.filter(m => !m._streaming)
          return [...filtered, { role: 'assistant', content: partial, _streaming: true, _tabId: currentTab }]
        })
      })

      setAiMessages(prev => {
        const filtered = prev.filter(m => !m._streaming)
        return [...filtered, { role: 'assistant', content: accumulated, _tabId: currentTab }]
      })
      api.getShellChatHistory().then(d => {
        setAiTokens(d.tokens || 0)
        setAiAtLimit(d.at_limit || false)
      }).catch(() => {})
    } catch (err) {
      if (err.message?.includes('context limit')) {
        setAiAtLimit(true)
      }
      setAiMessages(prev => [...prev.filter(m => !m._streaming), { role: 'assistant', content: `Erreur: ${err.message}` }])
    }
    setAiLoading(false)
    aiLoadingRef.current = false
  }, [api, t, aiAtLimit, focusAiTerminal])

  const handleAiSend = () => _sendAiMessage(aiInput, false)

  useEffect(() => {
    const handler = (e) => {
      const msg = e.detail?.message
      if (!msg) return
      setAiInput(msg)
      setTimeout(() => _sendAiMessage(msg, true), 100)
    }
    window.addEventListener('ask-ai-terminal', handler)
    return () => window.removeEventListener('ask-ai-terminal', handler)
  }, [_sendAiMessage])

  const handleAnalyze = async () => {
    setAnalyzing(true)
    setAiMessages(prev => [...prev, { role: 'system', content: 'Analyse du système en cours...' }])
    try {
      const d = await api.analyzeSystem()
      if (d.analysis) {
        setAnalysisContent(d.analysis)
        localStorage.setItem('shell_analysis', d.analysis)
      }
      setAiMessages(prev => [...prev.filter(m => m.content !== 'Analyse du système en cours...'), {
        role: 'system',
        content: 'Analyse système terminée et sauvegardée. Le contexte système est maintenant disponible.'
      }])
    } catch (err) {
      setAiMessages(prev => prev.filter(m => m.content !== 'Analyse du système en cours...'))
    }
    setAnalyzing(false)
  }

  return (
    <div className="shell-layout">
      <div className="shell-terminal-col">
        <div className="shell-tabs-bar">
          <div className="shell-tabs">
            {tabs.map((tab, i) => (
              <div
                key={tab.id}
                className={`shell-tab ${activeTab === tab.id ? 'active' : ''}`}
                onClick={() => setActiveTab(tab.id)}
                onDoubleClick={(e) => startRename(tab.id, e)}
              >
                <span className={`connection-dot ${tab.connected ? 'on' : 'off'}`} />
                {tab.type === 'ssh' && <Globe size={12} />}
                {tab.type === 'local' && <Monitor size={12} />}
                {editingTab === tab.id ? (
                  <input
                    className="shell-tab-rename"
                    value={editName}
                    onChange={e => setEditName(e.target.value)}
                    onBlur={finishRename}
                    onKeyDown={e => { if (e.key === 'Enter') finishRename(); if (e.key === 'Escape') setEditingTab(null) }}
                    autoFocus
                    onClick={e => e.stopPropagation()}
                  />
                ) : (
                  <span className="shell-tab-name">{tab.name}</span>
                )}
                <span className="shell-tab-index">{i + 1}</span>
                {tabs.length > 1 && (
                  <button
                    className="shell-tab-close"
                    onClick={(e) => closeTab(tab.id, e)}
                    title={t('shell.closeTab')}
                  >
                    <X size={12} />
                  </button>
                )}
              </div>
            ))}
          </div>

          <div className="shell-tab-actions">
            {tabs.length < MAX_TABS && (
              <div className="shell-new-tab-wrapper">
                <button className="shell-new-tab-btn" onClick={() => setShowMenu(!showMenu)} title={t('shell.newTab')}>
                  <Plus size={16} />
                  <ChevronDown size={12} />
                </button>
                {showMenu && (
                  <>
                    <div className="shell-menu-overlay" onClick={() => setShowMenu(false)} />
                    <div className="shell-new-tab-menu">
                      <div className="shell-menu-label">{t('shell.systemTerminals')}</div>
                      {systemTerminals.map(st => (
                        <button
                          key={st.name}
                          className="shell-menu-item"
                          onClick={() => addLocalTab(st.shell, st.name)}
                        >
                          <Monitor size={14} />
                          <span>{st.name}</span>
                          <span className="shell-menu-item-sub">{st.shell}</span>
                        </button>
                      ))}
                      <div className="shell-menu-divider" />
                      <div className="shell-menu-label">{t('shell.savedConnections')}</div>
                      {sshConnections.length === 0 && (
                        <div className="shell-menu-empty">{t('shell.noConnections')}</div>
                      )}
                      {sshConnections.map(conn => (
                        <div key={conn.name} className="shell-menu-item-row">
                          <button
                            className="shell-menu-item"
                            onClick={() => addSSHTab(conn)}
                          >
                            <Globe size={14} />
                            <span>{conn.name}</span>
                            <span className="shell-menu-item-sub">{conn.user}@{conn.host}:{conn.port}</span>
                          </button>
                          <button
                            className="shell-menu-item-icon"
                            onClick={(e) => { e.stopPropagation(); deleteSSHConnection(conn.name) }}
                            title={t('shell.deleteConnection')}
                          >
                            <Trash2 size={12} />
                          </button>
                        </div>
                      ))}
                      <div className="shell-menu-divider" />
                      <button className="shell-menu-item accent" onClick={() => { setShowSshModal(true); setShowMenu(false) }}>
                        <Plus size={14} />
                        <span>{t('shell.addConnection')}</span>
                      </button>
                    </div>
                  </>
                )}
              </div>
            )}
          </div>
        </div>

        <div className="shell-xterm-wrapper">
          {tabs.map(tab => (
            <div
              key={tab.id}
              id={`terminal-${tab.id}`}
              className={`shell-xterm-instance${activeTab === tab.id ? ' active' : ''}`}
            />
          ))}
        </div>
      </div>

      <div className="shell-ai-col">
        <div className="ai-panel-header">
          <span>Analyste Système</span>
          <div style={{ display: 'flex', gap: 6 }}>
            <button
              className="shell-analyze-btn"
              onClick={() => setShowAnalysis(true)}
              disabled={!analysisContent}
              title="Voir l'analyse"
            >
              <Eye size={13} />
              Analyse
            </button>
            <button
              className="shell-analyze-btn"
              onClick={handleAnalyze}
              disabled={analyzing}
              title="Analyser le système"
            >
              <Search size={13} />
              {analyzing ? '...' : 'Analyser'}
            </button>
          </div>
        </div>
        <div className="shell-ai-token-bar">
          <div className="shell-ai-token-track">
            <div
              className={`shell-ai-token-fill ${aiTokens >= SHELL_MAX_TOKENS * 0.8 ? 'warn' : ''}`}
              style={{ width: `${Math.min(100, (aiTokens / SHELL_MAX_TOKENS) * 100)}%` }}
            />
          </div>
          <span className="shell-ai-token-text">{Math.round(aiTokens / 1000)}k/{Math.round(SHELL_MAX_TOKENS / 1000)}k</span>
        </div>
        <div className="ai-panel-messages" ref={aiMessagesRef}>
          {aiMessages.map((msg, i) => (
            <ShellAIMessage key={i} msg={msg} sendToTerminal={sendToTerminal} terminalTabId={msg._tabId || activeTab} />
          ))}
          {aiLoading && <div style={{ textAlign: 'center', padding: 8 }}><span className="spinner" /></div>}
        </div>
        <div className="ai-panel-input">
          <input
            value={aiInput}
            onChange={e => setAiInput(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && handleAiSend()}
            placeholder={aiAtLimit ? '/clear pour continuer' : t('shell.askAi')}
            disabled={aiAtLimit && aiInput !== '/clear'}
          />
          <button className="sm" onClick={handleAiSend} disabled={(!aiInput.trim() && !aiAtLimit) || (aiAtLimit && aiInput !== '/clear')}>{t('shell.send')}</button>
        </div>
      </div>

      {showAnalysis && analysisContent && (
        <div className="shell-modal-overlay" onClick={() => setShowAnalysis(false)}>
          <div className="shell-analysis-modal" onClick={e => e.stopPropagation()}>
            <div className="shell-analysis-modal-header">
              <span>Analyse Système</span>
              <button className="shell-tab-close" onClick={() => setShowAnalysis(false)}><X size={16} /></button>
            </div>
            <div className="shell-analysis-modal-body">
              {renderContent(analysisContent).map((part, i) =>
                part.type === 'code' ? (
                  <div key={i} className="shell-code-block">
                    {part.lang && <div className="shell-code-lang">{part.lang}</div>}
                    <pre><code>{part.content}</code></pre>
                  </div>
                ) : (
                  <span key={i} dangerouslySetInnerHTML={{ __html: formatText(part.content) }} />
                )
              )}
            </div>
          </div>
        </div>
      )}

      {showSshModal && (
        <div className="shell-modal-overlay" onClick={() => setShowSshModal(false)}>
          <div className="shell-modal" onClick={e => e.stopPropagation()}>
            <div className="shell-modal-header">{t('shell.addConnection')}</div>
            <div className="shell-modal-body">
              <label className="shell-modal-label">{t('shell.connectionName')}</label>
              <input
                value={sshForm.name}
                onChange={e => setSshForm(f => ({ ...f, name: e.target.value }))}
                placeholder="prod-server"
              />
              <label className="shell-modal-label">{t('shell.host')}</label>
              <input
                value={sshForm.host}
                onChange={e => setSshForm(f => ({ ...f, host: e.target.value }))}
                placeholder="192.168.1.100"
              />
              <div className="shell-modal-row">
                <div className="shell-modal-field">
                  <label className="shell-modal-label">{t('shell.port')}</label>
                  <input
                    type="number"
                    value={sshForm.port}
                    onChange={e => setSshForm(f => ({ ...f, port: parseInt(e.target.value) || 22 }))}
                  />
                </div>
                <div className="shell-modal-field">
                  <label className="shell-modal-label">{t('shell.user')}</label>
                  <input
                    value={sshForm.user}
                    onChange={e => setSshForm(f => ({ ...f, user: e.target.value }))}
                    placeholder="root"
                  />
                </div>
              </div>
              <label className="shell-modal-label">{t('shell.keyPath')} ({t('shell.local')})</label>
              <input
                value={sshForm.key_path}
                onChange={e => setSshForm(f => ({ ...f, key_path: e.target.value }))}
                placeholder="~/.ssh/id_rsa"
              />
            </div>
            <div className="shell-modal-footer">
              <button className="ghost" onClick={() => setShowSshModal(false)}>{t('shell.cancel')}</button>
              <button className="primary" onClick={saveSSHConnection}>{t('shell.save')}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}

function ShellAIMessage({ msg, sendToTerminal, terminalTabId }) {
  const role = msg.role === 'user' ? 'user' : msg.role === 'system' ? 'system' : 'assistant'
  const content = msg.content || ''

  if (role === 'user') {
    return <div className={`ai-message user`}>{content}</div>
  }

  if (role === 'system') {
    return <div className={`ai-message system`}>{content}</div>
  }

  const parts = renderContent(content)

  return (
    <div className={`ai-message assistant`}>
      {parts.map((part, i) => {
        if (part.type === 'code') {
          return (
            <div key={i} className="shell-code-block">
              {part.lang && <div className="shell-code-lang">{part.lang}</div>}
              <pre><code>{part.content}</code></pre>
              <div className="shell-code-actions">
                <button onClick={() => navigator.clipboard.writeText(part.content)} title="Copier">
                  <Copy size={12} /> Copier
                </button>
                <button onClick={() => sendToTerminal(part.content, terminalTabId)} title="Envoyer au terminal">
                  <Send size={12} /> Terminal
                </button>
              </div>
            </div>
          )
        }
        return <span key={i} dangerouslySetInnerHTML={{ __html: formatText(part.content) }} />
      })}
    </div>
  )
}
