'use client'

import { useState, useRef, useEffect, useCallback, useMemo } from 'react'
import { ArrowUp, Square, Plus, ChevronDown, X, FileIcon, ImageIcon, Check, Shield, ShieldOff, Sparkles, Download, RefreshCw } from 'lucide-react'
import { cn } from '@/lib/utils'
import { useI18n } from '@/components/providers/i18n-provider'
import { useContainerWidth } from '@/hooks/use-container-width'
import type { Session, Message } from '@/lib/types'
import { MarkdownRenderer } from '@/components/markdown-renderer'
import { SlashCommandMenu } from '@/components/chat/slash-command-menu'
import { useSlashCommands } from '@/hooks/use-slash-commands'
import { useModels } from '@/hooks/use-models'
import { filterCommands, type SlashCommand } from '@/lib/slash-commands'
import { UserMessage, AssistantMessage } from '@/components/chat/chat-messages'
import { exportSession } from '@/components/chat/chat-export'
import { formatModelShort } from '@/components/chat/chat-utils'

interface ChatViewProps {
  session: Session | null
  messages: Message[]
  streaming: boolean
  isThinking?: boolean
  error: string | null
  workspaceName?: string
  workspaceId?: string | null
  permissionMode?: string
  thinkingMode?: string
  onSendMessage: (content: string, permissionMode?: string, thinkingMode?: string, attachments?: Array<{ name: string; filename: string; mimeType: string; tier: string }>) => void
  onStopStreaming: () => void
  onNewSession: () => void
  onPermissionDecision?: (requestId: string, decision: 'allow' | 'allow_session' | 'deny') => void
  onModelChange?: (model: string) => void
  onPermissionModeChange?: (mode: string) => void
  onThinkingModeChange?: (mode: string) => void
  onRenameSession?: (title: string) => void
  onClearSession?: () => Promise<void>
}

// MODEL_OPTIONS loaded dynamically via useModels() hook inside the component.
// No hardcoded array here — all models (built-in + custom) come from /api/models.

export function ChatView({
  session,
  messages,
  streaming,
  isThinking = false,
  error,
  workspaceName = '',
  workspaceId = null,
  permissionMode = 'confirm',
  thinkingMode = 'auto',
  onSendMessage,
  onStopStreaming,
  onNewSession,
  onPermissionDecision,
  onModelChange,
  onPermissionModeChange,
  onThinkingModeChange,
  onRenameSession,
  onClearSession,
}: ChatViewProps) {
  const { t } = useI18n()
  const { models: MODEL_OPTIONS, refreshing: modelsRefreshing, refresh: refreshModels } = useModels()

  const currentModelMeta = useMemo(() =>
    MODEL_OPTIONS.find((m) => m.id === session?.model),
    [MODEL_OPTIONS, session?.model],
  )
  const currentModelSupportsThinking = !!currentModelMeta?.capabilities?.supportsThinking
  const [input, setInput] = useState('')
  const [modelOpen, setModelOpen] = useState(false)
  const [permOpen, setPermOpen] = useState(false)
  const [thinkOpen, setThinkOpen] = useState(false)
  const modelRef = useRef<HTMLDivElement>(null)
  const permRef = useRef<HTMLDivElement>(null)
  const thinkRef = useRef<HTMLDivElement>(null)
  const toolbarRef = useRef<HTMLDivElement>(null)
  const [attachments, setAttachments] = useState<{ name: string; path: string; filename: string; mimeType: string; tier: string; isImage: boolean }[]>([])
  const messagesEndRef = useRef<HTMLDivElement>(null)
  const messagesContainerRef = useRef<HTMLDivElement>(null)
  const userScrolledUpRef = useRef(false)
  const textareaRef = useRef<HTMLTextAreaElement>(null)
  const fileInputRef = useRef<HTMLInputElement>(null)
  const systemMsgRef = useRef<HTMLDivElement>(null)
  const modelPickerRef = useRef<HTMLDivElement>(null)
  const [isDragging, setIsDragging] = useState(false)
  const dragCounterRef = useRef(0)

  const toolbarWidth = useContainerWidth(toolbarRef)
  const isCompact = toolbarWidth > 0 && toolbarWidth <= 500

  // ── Slash Commands ──
  const allCommands = useSlashCommands(workspaceId)
  const [slashMenuOpen, setSlashMenuOpen] = useState(false)
  const [slashIndex, setSlashIndex] = useState(0)
  const [systemMsg, setSystemMsg] = useState<string | null>(null)
  const [modelPickerOpen, setModelPickerOpen] = useState(false)

  // Compute the query part after `/` and filtered commands
  const slashQuery = useMemo(() => {
    if (!input.startsWith('/')) return ''
    return input.slice(1).split(/\s/)[0]
  }, [input])

  const filteredCommands = useMemo(
    () => (slashMenuOpen ? filterCommands(allCommands, slashQuery) : []),
    [slashMenuOpen, allCommands, slashQuery]
  )

  // Open/close menu based on input
  // Only show the menu while user is still typing the command name (no space yet).
  // Once a space appears (user is typing args), close the menu so Enter executes the command.
  useEffect(() => {
    if (input.startsWith('/') && !input.includes('\n')) {
      const afterSlash = input.slice(1)
      const hasSpace = afterSlash.includes(' ')
      if (!hasSpace) {
        // Still typing command name — show autocomplete
        const matches = filterCommands(allCommands, afterSlash)
        setSlashMenuOpen(matches.length > 0)
        setSlashIndex(0)
      } else {
        // Command name complete, user is typing args — close menu
        setSlashMenuOpen(false)
      }
    } else {
      setSlashMenuOpen(false)
    }
  }, [input, allCommands])

  // Auto-dismiss short system messages after 4 seconds; long ones stay until user dismisses
  useEffect(() => {
    if (!systemMsg) return
    // Messages with code blocks, lists, or > 120 chars are considered "long" — don't auto-dismiss
    if (systemMsg.length > 120 || systemMsg.includes('```') || systemMsg.includes('\n-')) return
    const timer = setTimeout(() => setSystemMsg(null), 4000)
    return () => clearTimeout(timer)
  }, [systemMsg])

  // Dismiss any overlay (slash menu / system message / model picker) on Escape
  useEffect(() => {
    if (!slashMenuOpen && !systemMsg && !modelPickerOpen) return
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        if (slashMenuOpen) { setSlashMenuOpen(false); setInput('') }
        setSystemMsg(null)
        setModelPickerOpen(false)
      }
    }
    document.addEventListener('keydown', handler)
    return () => document.removeEventListener('keydown', handler)
  }, [slashMenuOpen, systemMsg, modelPickerOpen])

  // Close system message on click outside
  useEffect(() => {
    if (!systemMsg) return
    const handler = (e: MouseEvent) => {
      if (systemMsgRef.current && !systemMsgRef.current.contains(e.target as Node)) {
        setSystemMsg(null)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [systemMsg])

  // Close model picker on click outside
  useEffect(() => {
    if (!modelPickerOpen) return
    const handler = (e: MouseEvent) => {
      if (modelPickerRef.current && !modelPickerRef.current.contains(e.target as Node)) {
        setModelPickerOpen(false)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [modelPickerOpen])

  // Close slash menu on click outside input container
  const inputContainerRef = useRef<HTMLDivElement>(null)
  useEffect(() => {
    if (!slashMenuOpen) return
    const handler = (e: MouseEvent) => {
      if (inputContainerRef.current && !inputContainerRef.current.contains(e.target as Node)) {
        setSlashMenuOpen(false)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [slashMenuOpen])

  /** Execute a slash command. Returns true if handled (input should be cleared). */
  const executeCommand = useCallback(async (cmd: SlashCommand, arg?: string) => {
    if (!session) return

    switch (cmd.category) {
      case 'built-in':
        switch (cmd.name) {
          case 'clear':
            if (onClearSession) {
              await onClearSession()
              setSystemMsg('Session cleared.')
            }
            break
          case 'compact':
            try {
              const res = await fetch(`/api/sessions/${session.id}/compact`, { method: 'POST' })
              const data = await res.json()
              if (data.ok) {
                setSystemMsg(`Compacted ${data.compacted} messages.`)
                // Reload messages by dispatching event
                window.dispatchEvent(new CustomEvent('forge:session-reload', { detail: { sessionId: session.id } }))
              }
            } catch { setSystemMsg('Failed to compact session.') }
            break
          case 'cost': {
            let totalIn = 0
            let totalOut = 0
            for (const msg of messages) {
              totalIn += msg.inputTokens || 0
              totalOut += msg.outputTokens || 0
            }
            setSystemMsg(`Token usage — Input: ${totalIn.toLocaleString()} | Output: ${totalOut.toLocaleString()} | Total: ${(totalIn + totalOut).toLocaleString()}`)
            break
          }
          case 'diff':
            if (workspaceId) {
              try {
                const res = await fetch(`/api/workspaces/${workspaceId}/diff`)
                const data = await res.json()
                if (data.diff) {
                  setSystemMsg(`\`\`\`diff\n${data.diff.slice(0, 2000)}\n\`\`\``)
                } else {
                  setSystemMsg(data.message || 'No changes.')
                }
              } catch { setSystemMsg('Failed to run git diff.') }
            } else {
              setSystemMsg('No workspace selected.')
            }
            break
          case 'export':
            exportSession(session.id, session.title)
            break
          case 'init':
            if (workspaceId) {
              // Ensure .claude/ directory exists + fetch init templates
              let templateBlock = ''
              try {
                const res = await fetch(`/api/workspaces/${workspaceId}/init`, { method: 'POST' })
                const data = await res.json()
                if (data.templates && typeof data.templates === 'object') {
                  templateBlock = Object.entries(data.templates)
                    .map(([name, content]) => `<template file="${name}">\n${content}\n</template>`)
                    .join('\n\n')
                }
              } catch { /* proceed without templates as fallback */ }
              // Send interview trigger with templates attached
              const initMessage = templateBlock
                ? `/init — Please start the workspace setup interview. Ask me questions one at a time.\n\nAfter the interview, use these templates to generate the .claude/ config files. Replace <!-- [/init ...] --> placeholders with personalized content based on my answers. Keep all non-placeholder content unchanged.\n\n${templateBlock}`
                : '/init — Please start the workspace setup interview. Ask me questions one at a time to personalize my .claude/ config files.'
              onSendMessage(initMessage)
            } else {
              setSystemMsg('No workspace selected.')
            }
            break
          case 'memory':
            window.dispatchEvent(new CustomEvent('forge:slash-command', { detail: { command: 'memory' } }))
            break
          case 'model':
            if (arg) {
              // Try to find a matching model
              const match = MODEL_OPTIONS.find(
                (o) => o.id === arg || o.label.toLowerCase().includes(arg.toLowerCase())
              )
              if (match) {
                onModelChange?.(match.id)
                setSystemMsg(`Model switched to ${match.label}`)
              } else {
                setSystemMsg(`Unknown model: ${arg}`)
              }
            } else {
              // Open inline model picker
              setModelPickerOpen(true)
              setSystemMsg(null)
            }
            break
          case 'rename':
            if (arg) {
              onRenameSession?.(arg)
              setSystemMsg(`Session renamed to "${arg}"`)
            } else {
              setSystemMsg('Usage: /rename <new title>')
            }
            break
          case 'save-as-template':
            if (!arg) {
              setSystemMsg('Usage: /save-as-template <template name>')
            } else if (!workspaceId) {
              setSystemMsg('No workspace selected. Open a project first.')
            } else {
              try {
                const res = await fetch('/api/marketplace/save-from-workspace', {
                  method: 'POST',
                  headers: { 'Content-Type': 'application/json' },
                  body: JSON.stringify({ workspaceId, name: arg }),
                })
                if (res.ok) {
                  setSystemMsg(`✅ 已保存为方案：${arg}`)
                } else {
                  const data = await res.json()
                  setSystemMsg(`Failed to save template: ${data.error || 'Unknown error'}`)
                }
              } catch {
                setSystemMsg('Failed to save template. Please try again.')
              }
            }
            break
          case 'stop':
            onStopStreaming()
            break
          case 'workspace':
            window.dispatchEvent(new CustomEvent('forge:slash-command', { detail: { command: 'workspace' } }))
            break
        }
        break

      case 'skill':
        // Send the skill name as an instruction to the agent
        onSendMessage(`/skill ${cmd.name}${arg ? ' ' + arg : ''}`)
        break

      case 'agent':
        // Send a delegate instruction to the agent
        onSendMessage(`@${cmd.name}${arg ? ' ' + arg : ''}`)
        break

      case 'mcp':
        // MCP commands — placeholder for future MCP prompts support
        setSystemMsg(`MCP command /${cmd.name} — feature coming soon.`)
        break
    }
  }, [session, messages, onClearSession, onStopStreaming, onModelChange, onRenameSession, onSendMessage, workspaceId, allCommands])

  const handleSlashSelect = useCallback((cmd: SlashCommand) => {
    if (cmd.name === 'model') {
      // /model opens an inline picker instead of waiting for text arg
      executeCommand(cmd)
      setInput('')
      setSlashMenuOpen(false)
    } else if (cmd.hasArg) {
      // Fill the command with a space for the user to type the argument
      setInput(`/${cmd.name} `)
      setSlashMenuOpen(false)
      textareaRef.current?.focus()
    } else {
      executeCommand(cmd)
      setInput('')
      setSlashMenuOpen(false)
    }
  }, [executeCommand])

  // Auto-scroll: only scroll to bottom when user hasn't scrolled up.
  // On session change (messages replaced entirely), always scroll to bottom instantly.
  const prevMessageCountRef = useRef(0)
  useEffect(() => {
    const isSessionChange = prevMessageCountRef.current === 0 && messages.length > 0
    const isNewMessage = messages.length > prevMessageCountRef.current && prevMessageCountRef.current > 0
    prevMessageCountRef.current = messages.length

    if (isSessionChange) {
      // Session loaded — always snap to bottom instantly, reset scroll state
      userScrolledUpRef.current = false
      messagesEndRef.current?.scrollIntoView({ behavior: 'instant' as ScrollBehavior })
    } else if ((isNewMessage || streaming) && !userScrolledUpRef.current) {
      // New message added or streaming content updating — smooth scroll to bottom
      messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
    }
    // If user scrolled up or message count decreased (clear), do nothing
  }, [messages, streaming])

  // Track whether user has scrolled up away from the bottom
  useEffect(() => {
    const container = messagesContainerRef.current
    if (!container) return
    const handleScroll = () => {
      const { scrollTop, scrollHeight, clientHeight } = container
      // Consider "at bottom" if within 80px of the bottom
      userScrolledUpRef.current = scrollHeight - scrollTop - clientHeight > 80
    }
    container.addEventListener('scroll', handleScroll, { passive: true })
    return () => container.removeEventListener('scroll', handleScroll)
  }, [])

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

  // Close model dropdown on outside click
  useEffect(() => {
    if (!modelOpen) return
    const handler = (e: MouseEvent) => {
      if (modelRef.current && !modelRef.current.contains(e.target as Node)) setModelOpen(false)
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [modelOpen])

  // Close permission dropdown on outside click
  useEffect(() => {
    if (!permOpen) return
    const handler = (e: MouseEvent) => {
      if (permRef.current && !permRef.current.contains(e.target as Node)) setPermOpen(false)
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [permOpen])

  // Close thinking dropdown on outside click
  useEffect(() => {
    if (!thinkOpen) return
    const handler = (e: MouseEvent) => {
      if (thinkRef.current && !thinkRef.current.contains(e.target as Node)) setThinkOpen(false)
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [thinkOpen])

  const handleFileUpload = useCallback(async (files: FileList | null) => {
    if (!files) return
    for (const file of Array.from(files)) {
      const formData = new FormData()
      formData.append('file', file)
      try {
        const res = await fetch('/api/upload', { method: 'POST', body: formData })
        if (!res.ok) continue
        const data = await res.json()
        setAttachments(prev => [...prev, {
          name: data.originalName,
          path: data.path,
          filename: data.filename,
          mimeType: data.mimeType,
          tier: data.tier,
          isImage: data.isImage,
        }])
      } catch { /* ignore */ }
    }
    // Reset file input so the same file can be selected again
    if (fileInputRef.current) fileInputRef.current.value = ''
  }, [])

  const removeAttachment = useCallback((idx: number) => {
    setAttachments(prev => prev.filter((_, i) => i !== idx))
  }, [])

  // ── Drag & Drop image upload ──
  useEffect(() => {
    if (!session) return

    const handleDragOver = (e: DragEvent) => {
      e.preventDefault()
    }

    const handleDragEnter = (e: DragEvent) => {
      e.preventDefault()
      dragCounterRef.current++
      const hasFiles = e.dataTransfer?.types.includes('Files')
      if (hasFiles) setIsDragging(true)
    }

    const handleDragLeave = (e: DragEvent) => {
      dragCounterRef.current--
      if (dragCounterRef.current <= 0) {
        setIsDragging(false)
        dragCounterRef.current = 0
      }
    }

    const handleDrop = (e: DragEvent) => {
      e.preventDefault()
      setIsDragging(false)
      dragCounterRef.current = 0

      const files = e.dataTransfer?.files
      if (!files || files.length === 0) return

      const imageFiles = Array.from(files).filter((f) => f.type.startsWith('image/'))
      if (imageFiles.length === 0) return

      const dt = new DataTransfer()
      for (const f of imageFiles) dt.items.add(f)
      handleFileUpload(dt.files)
    }

    document.addEventListener('dragover', handleDragOver)
    document.addEventListener('dragenter', handleDragEnter)
    document.addEventListener('dragleave', handleDragLeave)
    document.addEventListener('drop', handleDrop)

    return () => {
      document.removeEventListener('dragover', handleDragOver)
      document.removeEventListener('dragenter', handleDragEnter)
      document.removeEventListener('dragleave', handleDragLeave)
      document.removeEventListener('drop', handleDrop)
    }
  }, [session, handleFileUpload])

  const handleSend = () => {
    if ((!input.trim() && attachments.length === 0) || streaming) return

    // Handle slash command execution
    if (input.startsWith('/') && !input.includes('\n')) {
      const parts = input.slice(1).split(/\s+/)
      const cmdName = parts[0]
      const arg = parts.slice(1).join(' ').trim() || undefined
      const cmd = allCommands.find((c) => c.name === cmdName)
      if (cmd) {
        executeCommand(cmd, arg)
        setInput('')
        setSlashMenuOpen(false)
        return
      }
      // Not a recognized command — fall through and send as normal message
    }

    // Pass attachments as structured data for multimodal processing
    const attachmentData = attachments.length > 0
      ? attachments.map(a => ({ name: a.name, filename: a.filename, mimeType: a.mimeType, tier: a.tier }))
      : undefined

    // Build display text (user sees file names in their message)
    let content = input
    if (attachments.length > 0 && !content.trim()) {
      content = attachments.map(a => `[${a.name}]`).join(' ')
    }

    onSendMessage(content, undefined, undefined, attachmentData)
    setInput('')
    setAttachments([])
    // User sent a message — reset scroll state so auto-scroll follows the new response
    userScrolledUpRef.current = false
  }

  if (!session) {
    return (
      <div className="flex flex-col items-center justify-center h-full gap-4">
        <img src="/mascot.png" alt="Forge" className="w-20 h-20 object-contain" />
        <p className="text-[14px] text-secondary">{t('chat.startNew')}</p>
        <button
          onClick={onNewSession}
          className="flex items-center gap-2 px-4 py-2 rounded-lg bg-brand-primary text-on-primary text-[13px] font-medium hover:bg-brand-primary-hover active:bg-brand-primary-active active:scale-[0.97] transition-colors focus:outline-none focus-visible:shadow-focus-ring"
        >
          <Plus size={14} />
          {t('chat.newSession')}
        </button>
      </div>
    )
  }

  return (
    <div className="flex flex-col h-full">
      {/* Chat Header */}
      <div ref={toolbarRef} className="flex items-center justify-between px-5 h-[52px] border-b border-subtle shrink-0 min-w-0">
        <div className="flex items-center gap-3 min-w-0 flex-1 overflow-hidden">
          <span className="text-[15px] font-semibold text-primary truncate">
            {session.title}
          </span>
          {!isCompact && workspaceName && (
            <span className="text-[11px] px-2 py-0.5 rounded bg-surface-active text-tertiary shrink-0 whitespace-nowrap">
              {workspaceName}
            </span>
          )}
        </div>
        <div className="flex items-center gap-3 shrink-0">
          <button
            onClick={() => exportSession(session.id, session.title)}
            className="flex items-center justify-center w-7 h-7 rounded-md hover:bg-surface-hover transition-colors"
            title={t('button.exportSession')}
          >
            <Download size={14} className="text-tertiary" />
          </button>
        </div>
      </div>

      {/* Messages Area */}
      <div ref={messagesContainerRef} className="flex-1 overflow-y-auto px-6 py-4">
        {messages.length === 0 && !streaming && (
          <div className="flex flex-col items-center justify-center h-full gap-5">
            <img src="/mascot.png" alt="Forge" className="w-20 h-20 object-contain" />
            <div className="text-center space-y-1.5">
              <p className="text-[14px] text-muted">{t('chat.suggestions')}</p>
            </div>
            <div className="flex flex-wrap gap-2 justify-center max-w-[400px]">
              {[
                t('chat.suggestSummarize'),
                t('chat.suggestBug'),
                t('chat.suggestRefactor'),
              ].map((suggestion) => (
                <button
                  key={suggestion}
                  onClick={() => { setInput(suggestion); textareaRef.current?.focus() }}
                  className="px-3 py-1.5 rounded-lg border border-subtle text-[12px] text-secondary hover:bg-surface-hover hover:border-indigo/30 active:bg-surface-active transition-colors focus:outline-none focus-visible:shadow-focus-ring"
                >
                  {suggestion}
                </button>
              ))}
            </div>
          </div>
        )}

        {(() => {
          // Only the LAST assistant message can be in streaming state.
          // Using temp- prefix alone is dangerous: interrupted streams leave orphaned temp IDs
          // that would incorrectly match on the next send.
          const lastAssistantIdx = messages.reduce((acc, m, i) => m.role === 'assistant' ? i : acc, -1)
          return messages.map((msg, i) => (
            <div key={msg.id} className="mb-6">
              {msg.role === 'user' ? (
                <UserMessage blocks={msg.blocks} fallbackContent={msg.content} />
              ) : (
                <AssistantMessage blocks={msg.blocks} streaming={streaming && i === lastAssistantIdx} isThinking={isThinking && streaming && i === lastAssistantIdx} thinkingMode={thinkingMode} onPermissionDecision={onPermissionDecision} elapsedSeconds={msg.elapsedSeconds} inputTokens={msg.inputTokens} outputTokens={msg.outputTokens} />
              )}
            </div>
          ))
        })()}

        {error && (
          <div className="mb-6 px-4 py-3 rounded-lg bg-coral/10 border border-coral/30 animate-fade-in">
            <p className="text-[12px] text-coral">{error}</p>
          </div>
        )}

        <div ref={messagesEndRef} />
      </div>

      {/* Input Area */}
      <div className="px-5 pb-3 flex flex-col gap-0">
        {/* System message from slash commands */}
        {systemMsg && (
          <div
            ref={systemMsgRef}
            className="mb-2 rounded-lg bg-surface border border-subtle animate-fade-in"
          >
            <div className="px-4 py-2.5 max-h-[320px] overflow-y-auto text-[12px] text-secondary whitespace-pre-wrap">
              <MarkdownRenderer content={systemMsg} />
            </div>
          </div>
        )}
        {/* Model picker from /model command */}
        {modelPickerOpen && (
          <div ref={modelPickerRef} className="mb-2 rounded-lg bg-surface border border-subtle animate-fade-in">
            <div className="px-4 py-2 border-b border-subtle">
              <span className="text-[12px] font-medium text-secondary">{t('chat.selectModel')}</span>
            </div>
            <div className="max-h-[240px] overflow-y-auto py-1">
              {(() => {
                const providers = [...new Set(MODEL_OPTIONS.map(o => o.provider))]
                return providers.map(provider => (
                  <div key={provider}>
                    <div className="px-4 py-1 text-[10px] font-medium text-muted uppercase tracking-wide">{provider}</div>
                    {MODEL_OPTIONS.filter(o => o.provider === provider).map(opt => (
                      <button
                        key={opt.id}
                        onClick={() => {
                          onModelChange?.(opt.id)
                          setModelPickerOpen(false)
                          setSystemMsg(`Model switched to ${opt.label}`)
                        }}
                        className="w-full text-left px-4 py-1.5 text-[12px] text-secondary hover:bg-hover hover:text-primary transition-colors"
                      >
                        {opt.label}
                      </button>
                    ))}
                  </div>
                ))
              })()}
            </div>
          </div>
        )}
        {/* Attachment preview */}
        {attachments.length > 0 && (
          <div className="flex gap-2 pb-2 flex-wrap items-center">
            {attachments.map((att, i) => (
              att.isImage ? (
                <div key={i} className="relative group">
                  <img
                    src={`/api/upload/${att.filename}`}
                    alt={att.name}
                    className="w-14 h-14 rounded-lg object-cover border border-subtle"
                  />
                  <button
                    onClick={() => removeAttachment(i)}
                    className="absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full bg-coral text-white flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity shadow-sm"
                  >
                    <X size={10} />
                  </button>
                </div>
              ) : (
                <div key={i} className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-surface border border-subtle text-[11px]">
                  <FileIcon size={12} className="text-tertiary" />
                  <span className="text-secondary max-w-[120px] truncate">{att.name}</span>
                  <button onClick={() => removeAttachment(i)} className="text-muted hover:text-coral">
                    <X size={10} />
                  </button>
                </div>
              )
            ))}
          </div>
        )}
        <input
          ref={fileInputRef}
          type="file"
          multiple
          className="hidden"
          onChange={(e) => handleFileUpload(e.target.files)}
        />
        {/* Input Container — rounded box with textarea + toolbar */}
        <div
          ref={inputContainerRef}
          className={cn(
            'flex flex-col rounded-xl bg-elevated border shadow-forge-sm focus-within:border-brand-primary focus-within:shadow-forge-hover transition-colors relative',
            isDragging ? 'border-indigo border-dashed bg-indigo/[0.03]' : 'border-subtle'
          )}
        >
          {/* Drag overlay */}
          {isDragging && (
            <div className="absolute inset-0 z-50 rounded-xl flex items-center justify-center pointer-events-none">
              <div className="flex items-center gap-2 text-indigo">
                <ImageIcon size={20} />
                <span className="text-[13px] font-medium">{t('chat.dropImages')}</span>
              </div>
            </div>
          )}
          {/* Slash command autocomplete menu */}
          {slashMenuOpen && (
            <SlashCommandMenu
              commands={filteredCommands}
              selectedIndex={slashIndex}
              onSelect={handleSlashSelect}
            />
          )}
          {/* Textarea Area */}
          <div className="px-3.5 pt-3 pb-2 min-h-[80px]">
            <textarea
              ref={textareaRef}
              value={input}
              onChange={(e) => setInput(e.target.value)}
              placeholder={t('input.reply')}
              rows={1}
              className="w-full bg-transparent text-[13px] text-primary placeholder:text-muted outline-none resize-none leading-relaxed"
              onPaste={(e) => {
                const items = e.clipboardData?.items
                if (!items) return
                const files: File[] = []
                for (const item of items) {
                  if (item.kind === 'file' && item.type.startsWith('image/')) {
                    const file = item.getAsFile()
                    if (file) files.push(file)
                  }
                }
                if (files.length > 0) {
                  e.preventDefault()
                  const dt = new DataTransfer()
                  for (const f of files) dt.items.add(f)
                  handleFileUpload(dt.files)
                }
              }}
              onKeyDown={(e) => {
                // Slash command menu keyboard navigation
                if (slashMenuOpen && filteredCommands.length > 0) {
                  if (e.key === 'ArrowDown') {
                    e.preventDefault()
                    setSlashIndex((i) => Math.min(i + 1, filteredCommands.length - 1))
                    return
                  }
                  if (e.key === 'ArrowUp') {
                    e.preventDefault()
                    setSlashIndex((i) => Math.max(i - 1, 0))
                    return
                  }
                  if (e.key === 'Tab') {
                    e.preventDefault()
                    handleSlashSelect(filteredCommands[slashIndex])
                    return
                  }
                  if (e.key === 'Escape') {
                    e.preventDefault()
                    setSlashMenuOpen(false)
                    return
                  }
                  if (e.key === 'Enter' && !e.shiftKey) {
                    e.preventDefault()
                    handleSlashSelect(filteredCommands[slashIndex])
                    return
                  }
                }
                if (e.key === 'Enter' && !e.shiftKey) {
                  e.preventDefault()
                  handleSend()
                }
              }}
            />
          </div>
          {/* Toolbar Row */}
          <div className="flex items-center justify-between px-2.5 h-10">
            {/* Left Tools */}
            <div className="flex items-center gap-1.5">
              <button
                onClick={() => fileInputRef.current?.click()}
                className="flex items-center justify-center w-7 h-7 rounded-lg hover:bg-surface-hover transition-colors"
                title={t('button.attachFile')}
              >
                <Plus size={16} className="text-tertiary" />
              </button>
              {/* Permission Mode Dropdown */}
              <div className="relative" ref={permRef}>
                <button
                  onClick={() => setPermOpen(!permOpen)}
                  className={cn(
                    'flex items-center gap-1 px-2 py-1 rounded-md transition-colors text-[11px] font-medium focus:outline-none focus-visible:shadow-focus-ring',
                    permissionMode === 'full'
                      ? 'bg-surface-active text-amber hover:bg-surface-hover'
                      : 'bg-surface-active text-secondary hover:bg-surface-hover'
                  )}
                >
                  {permissionMode === 'full' ? <ShieldOff size={12} className="text-amber" /> : <Shield size={12} className="text-indigo" />}
                  <span>{permissionMode === 'full' ? t('permission.fullAccess') : t('permission.ask')}</span>
                  <ChevronDown size={10} className={cn('text-tertiary transition-transform', permOpen && 'rotate-180')} />
                </button>
                {permOpen && (
                  <div className="absolute left-0 bottom-full mb-1 w-[200px] bg-surface border border-subtle rounded-[10px] shadow-lg z-50 p-1 animate-slide-down">
                    <button
                      onClick={() => { onPermissionModeChange?.('confirm'); setPermOpen(false) }}
                      className={cn(
                        'flex items-center gap-2 w-full px-2.5 h-9 rounded-md transition-colors text-left',
                        permissionMode === 'confirm' ? 'bg-elevated' : 'hover:bg-surface-hover'
                      )}
                    >
                      <Shield size={14} className="text-indigo shrink-0" />
                      <span className={cn('text-[12px] font-medium flex-1', permissionMode === 'confirm' ? 'text-primary' : 'text-secondary')}>{t('permission.ask')}</span>
                      {permissionMode === 'confirm' && <Check size={14} className="text-indigo shrink-0" />}
                    </button>
                    <button
                      onClick={() => { onPermissionModeChange?.('full'); setPermOpen(false) }}
                      className={cn(
                        'flex items-center gap-2 w-full px-2.5 h-9 rounded-md transition-colors text-left',
                        permissionMode === 'full' ? 'bg-elevated' : 'hover:bg-surface-hover'
                      )}
                    >
                      <ShieldOff size={14} className="text-amber shrink-0" />
                      <span className={cn('text-[12px] font-medium flex-1', permissionMode === 'full' ? 'text-primary' : 'text-secondary')}>{t('permission.fullAccess')}</span>
                      {permissionMode === 'full' && <Check size={14} className="text-amber shrink-0" />}
                    </button>
                  </div>
                )}
              </div>
            </div>
            {/* Right Tools */}
            <div className="flex items-center gap-2">
              {/* Thinking Mode Pill */}
              <div className="relative" ref={thinkRef}>
                <button
                  onClick={() => currentModelSupportsThinking && setThinkOpen(!thinkOpen)}
                  disabled={!currentModelSupportsThinking}
                  title={currentModelSupportsThinking ? undefined : t('chat.thinkingUnsupported')}
                  className={cn(
                    'flex items-center gap-1 px-2 py-1 rounded-md border transition-colors',
                    thinkOpen
                      ? 'border-indigo bg-indigo/10'
                      : 'border-subtle hover:bg-surface-hover',
                    !currentModelSupportsThinking && 'opacity-50 cursor-not-allowed'
                  )}
                >
                  <Sparkles size={12} className={cn(thinkOpen ? 'text-indigo' : 'text-tertiary')} />
                  <span className={cn('text-[11px] font-medium', thinkOpen ? 'text-indigo' : 'text-secondary')}>
                    {thinkingMode === 'max' ? 'Max' : thinkingMode === 'off' ? 'Off' : 'Auto'}
                  </span>
                  <ChevronDown size={10} className={cn('transition-transform', thinkOpen ? 'text-indigo rotate-180' : 'text-tertiary')} />
                </button>
                {thinkOpen && (
                  <div className="absolute right-0 bottom-full mb-1 w-[160px] bg-surface border border-subtle rounded-[10px] shadow-lg z-50 p-1 animate-slide-down">
                    {[
                      { value: 'off', label: 'Off' },
                      { value: 'auto', label: 'Auto' },
                      { value: 'max', label: 'Max' },
                    ].map((opt) => (
                      <button
                        key={opt.value}
                        onClick={() => { onThinkingModeChange?.(opt.value); setThinkOpen(false) }}
                        className={cn(
                          'flex items-center gap-2 w-full px-2.5 h-8 rounded-md transition-colors text-left',
                          thinkingMode === opt.value ? 'bg-elevated' : 'hover:bg-surface-hover'
                        )}
                      >
                        <span className={cn('text-[12px] font-medium flex-1', thinkingMode === opt.value ? 'text-primary' : 'text-secondary')}>{opt.label}</span>
                        {thinkingMode === opt.value && <Check size={14} className="text-indigo shrink-0" />}
                      </button>
                    ))}
                  </div>
                )}
              </div>
              <div className="relative" ref={modelRef}>
                <button
                  onClick={() => setModelOpen(!modelOpen)}
                  className={cn(
                    'flex items-center gap-1 px-2.5 py-1 rounded-md border transition-colors',
                    modelOpen
                      ? 'border-indigo bg-indigo/10'
                      : 'border-subtle hover:bg-surface-hover'
                  )}
                >
                  <span className={cn('text-[11px] font-medium', modelOpen ? 'text-indigo font-semibold' : 'text-secondary')}>
                    {formatModelShort(session.model)}
                  </span>
                  <ChevronDown size={10} className={cn('transition-transform', modelOpen ? 'text-indigo rotate-180' : 'text-tertiary')} />
                </button>
                {modelOpen && (
                  <div className="absolute right-0 bottom-full mb-1 w-[240px] bg-surface border border-subtle rounded-[10px] shadow-lg z-50 p-1 max-h-[320px] overflow-y-auto animate-slide-down">
                    <div className="flex items-center justify-between px-2.5 pt-1.5 pb-1">
                      <span className="text-[10px] font-semibold text-muted tracking-wide">{t('chat.selectModel')}</span>
                      <button
                        onClick={(e) => { e.stopPropagation(); refreshModels() }}
                        disabled={modelsRefreshing}
                        className="p-1 rounded hover:bg-surface-hover disabled:opacity-50"
                        title={t('chat.refreshModels')}
                      >
                        <RefreshCw size={12} className={cn('text-tertiary', modelsRefreshing && 'animate-spin')} />
                      </button>
                    </div>
                    {(() => {
                      const providers = [...new Set(MODEL_OPTIONS.map((o) => o.provider))]
                      const isCurrentAvailable = MODEL_OPTIONS.some((o) => o.id === session.model)
                      return (
                        <>
                          {!isCurrentAvailable && session.model && (
                            <div className="mx-2 mb-1 px-2 py-1 rounded bg-coral/10 text-[10px] text-coral">
                              {t('chat.currentModelUnavailable')}
                            </div>
                          )}
                          {providers.map((provider, idx) => (
                            <div key={provider}>
                              {idx > 0 && <div className="mx-2 my-1 h-px bg-subtle" />}
                              <div className="px-2.5 pt-1.5 pb-0.5">
                                <span className="text-[10px] font-semibold text-muted tracking-wide">{provider}</span>
                              </div>
                              {MODEL_OPTIONS.filter((o) => o.provider === provider).map((opt) => (
                                <button
                                  key={opt.id}
                                  onClick={() => { onModelChange?.(opt.id); setModelOpen(false) }}
                                  className={cn(
                                    'flex items-center gap-2 w-full px-2.5 h-8 rounded-md transition-colors text-left',
                                    session.model === opt.id ? 'bg-elevated' : 'hover:bg-surface-hover'
                                  )}
                                >
                                  <span className={cn('text-[12px] font-medium flex-1', session.model === opt.id ? 'text-primary' : 'text-secondary')}>
                                    {opt.provider === 'Anthropic' ? formatModelShort(opt.id) : opt.label}
                                  </span>
                                  {opt.capabilities?.supportsThinking && <Sparkles size={10} className="text-indigo shrink-0" />}
                                  {session.model === opt.id && <Check size={14} className="text-indigo shrink-0" />}
                                </button>
                              ))}
                            </div>
                          ))}
                        </>
                      )
                    })()}
                  </div>
                )}
              </div>
              {streaming ? (
                <button
                  onClick={onStopStreaming}
                  className="flex items-center justify-center w-8 h-8 rounded-lg bg-coral hover:bg-coral/80 active:bg-coral/70 active:scale-[0.97] transition-colors"
                >
                  <Square size={14} className="text-white" fill="white" />
                </button>
              ) : (
                <button
                  onClick={handleSend}
                  disabled={!input.trim() && attachments.length === 0}
                  className="flex items-center justify-center w-8 h-8 rounded-lg bg-brand-primary hover:bg-brand-primary-hover active:bg-brand-primary-active active:scale-[0.97] transition-colors disabled:opacity-40 focus:outline-none focus-visible:shadow-focus-ring"
                >
                  <ArrowUp size={16} className="text-white" />
                </button>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

