import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import {
  Menu, Settings, Plus, Trash2, Send, Paperclip, X, RefreshCw,
  Bot, ChevronDown, Database, Wrench, Loader2, Search,
} from 'lucide-react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { useAppStore } from '@/stores/app';
import { useI18n } from '@/i18n';
import {
  chatApi,
  type ChatSession,
  type ChatSendBody,
} from '@/api/endpoints/chat';

/* ── Local types ── */

interface UIMessage {
  role: 'user' | 'assistant';
  content: string;
  timestamp: string;
  failed?: boolean;
  thinking?: boolean;
}

interface AttachedFile {
  file: File;
  name: string;
  type: string;
  preview?: string;
}

interface ChatParams {
  temperature: number;
  max_tokens: number;
  top_p: number;
  top_k: number;
  frequency_penalty: number;
  presence_penalty: number;
  reasoning_effort: string;
}

const ACCEPT =
  'image/*,.pdf,.txt,.csv,.json,.log,.xml,.yaml,.yml,.md,.py,.js,.ts,.html,.css,.sql,.tf,.hcl';

const DEFAULT_PARAMS: ChatParams = {
  temperature: 1,
  max_tokens: 6000,
  top_p: 0.95,
  top_k: 1,
  frequency_penalty: 0,
  presence_penalty: 0,
  reasoning_effort: 'medium',
};

/* ── Helpers ── */

function nowHHmm(): string {
  return new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
}

function timeAgo(dateStr: string): string {
  const tFn = useI18n.getState().t;
  const d = new Date(dateStr.endsWith('Z') ? dateStr : dateStr + 'Z');
  const diff = (Date.now() - d.getTime()) / 1000;
  if (diff < 60) return tFn('chat.timeNow');
  if (diff < 3600) return `${Math.floor(diff / 60)}min`;
  if (diff < 86400) return `${Math.floor(diff / 3600)}h`;
  return `${Math.floor(diff / 86400)}d`;
}

function dateGroup(dateStr: string): string {
  const tFn = useI18n.getState().t;
  if (!dateStr) return tFn('chat.other');
  const d = new Date(dateStr.endsWith('Z') ? dateStr : dateStr + 'Z');
  const diff = (Date.now() - d.getTime()) / 86400000;
  if (diff < 1) return tFn('chat.today');
  if (diff < 2) return tFn('chat.yesterday');
  if (diff < 7) return tFn('chat.last7');
  if (diff < 30) return tFn('chat.last30');
  return tFn('chat.older');
}

function groupSessions(sessions: ChatSession[]): Record<string, ChatSession[]> {
  const groups: Record<string, ChatSession[]> = {};
  for (const s of sessions) {
    const g = dateGroup(s.updated_at || s.created_at);
    (groups[g] ??= []).push(s);
  }
  return groups;
}

/* ── Component ── */

export default function ChatPage() {
  /* ── Store ── */
  const { ociCfg, genaiCfg, models, mcpSvr, adbCfg } = useAppStore();
  const { t } = useI18n();

  /* ── State ── */
  const [messages, setMessages] = useState<UIMessage[]>([]);
  const [sessionId, setSessionId] = useState<string | null>(null);
  const [sessions, setSessions] = useState<ChatSession[]>([]);
  const [model, setModel] = useState('');
  const [ociConfig, setOciConfig] = useState('');
  const [files, setFiles] = useState<AttachedFile[]>([]);
  const [chatParams, setChatParams] = useState<ChatParams>({ ...DEFAULT_PARAMS });
  const [useTools, setUseTools] = useState(true);
  const [historyOpen, setHistoryOpen] = useState(false);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [sending, setSending] = useState(false);
  const [modelDropOpen, setModelDropOpen] = useState(false);
  const [modelSearch, setModelSearch] = useState('');

  const inputRef = useRef<HTMLTextAreaElement>(null);
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const modelDropRef = useRef<HTMLDivElement>(null);
  const pollRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  /* ── Derived ── */
  const isDirect = !!model && !model.startsWith('cfg:');

  const modelInfo = useMemo(() => {
    let mid = '';
    if (model.startsWith('cfg:')) {
      const g = genaiCfg.find((x) => x.id === model.slice(4));
      if (g) mid = g.model_id;
    } else {
      mid = model;
    }
    return models[mid] || ({} as { provider?: string; reasoning?: boolean; max_tokens?: number; penalties?: boolean });
  }, [model, models, genaiCfg]);

  const isReasoning = !!(modelInfo as any).reasoning;
  const maxTokenLimit = (modelInfo as any).max_tokens || 128000;
  const hasTopK = ['meta', 'google', ''].includes((modelInfo as any).provider || '');
  const hasPenalties = !!(modelInfo as any).penalties;

  const toolCount = useMemo(
    () =>
      mcpSvr
        .filter((m) => m.is_active && Array.isArray(m.tools) && m.tools.length)
        .reduce((a, m) => a + (m.tools?.length || 0), 0),
    [mcpSvr],
  );

  const hasRag = useMemo(
    () => adbCfg.some((c) => c.is_active) && (genaiCfg.length > 0 || ociCfg.length > 0),
    [adbCfg, genaiCfg, ociCfg],
  );

  const modelLabel = useMemo(() => {
    if (!model) return t('chat.selectModel');
    if (model.startsWith('cfg:')) {
      const g = genaiCfg.find((x) => x.id === model.slice(4));
      if (!g) return 'Config...';
      const mi = models[g.model_id];
      return `${g.name || mi?.name || g.model_id} (${g.genai_region})`;
    }
    const mi = models[model];
    return mi?.name || model;
  }, [model, genaiCfg, models, t]);

  /* Model dropdown items grouped by provider */
  const modelGroups = useMemo(() => {
    const groups: { label: string; items: { value: string; name: string }[] }[] = [];
    const q = modelSearch.toLowerCase();

    // GenAI configs
    if (genaiCfg.length) {
      const items = genaiCfg
        .map((g) => {
          const mi = models[g.model_id];
          const name = `${g.name || mi?.name || g.model_id} (${g.genai_region})`;
          return { value: `cfg:${g.id}`, name };
        })
        .filter((i) => !q || i.name.toLowerCase().includes(q));
      if (items.length) groups.push({ label: t('chat.savedConfigs'), items });
    }

    // Direct models grouped by provider
    const provs: Record<string, { value: string; name: string }[]> = {};
    for (const [mid, info] of Object.entries(models)) {
      const p = info.provider || 'other';
      (provs[p] ??= []).push({ value: mid, name: info.name });
    }
    const order = ['openai', 'google', 'meta', 'xai', 'cohere'];
    const seen = new Set<string>();
    for (const p of [...order, ...Object.keys(provs)]) {
      if (seen.has(p) || !provs[p]) continue;
      seen.add(p);
      const items = provs[p].filter((i) => !q || i.name.toLowerCase().includes(q));
      if (items.length) {
        groups.push({ label: p.charAt(0).toUpperCase() + p.slice(1), items });
      }
    }
    return groups;
  }, [models, genaiCfg, modelSearch, t]);

  const sessionGroups = useMemo(() => groupSessions(sessions), [sessions]);

  /* ── Effects ── */

  // Load sessions on mount
  useEffect(() => {
    chatApi.listSessions('chat', 50).then(setSessions).catch(() => {});
  }, []);

  // Close model dropdown on outside click
  useEffect(() => {
    function handleClick(e: MouseEvent) {
      if (modelDropRef.current && !modelDropRef.current.contains(e.target as Node)) {
        setModelDropOpen(false);
      }
    }
    document.addEventListener('mousedown', handleClick);
    return () => document.removeEventListener('mousedown', handleClick);
  }, []);

  // Keyboard shortcuts
  useEffect(() => {
    function handleKey(e: KeyboardEvent) {
      if (e.key === 'Escape') {
        setHistoryOpen(false);
        setSettingsOpen(false);
        setModelDropOpen(false);
      }
    }
    document.addEventListener('keydown', handleKey);
    return () => document.removeEventListener('keydown', handleKey);
  }, []);

  // Auto-scroll
  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  // Cleanup poll on unmount
  useEffect(() => {
    return () => {
      if (pollRef.current) clearTimeout(pollRef.current);
    };
  }, []);

  /* ── Actions ── */

  const refreshSessions = useCallback(() => {
    chatApi.listSessions('chat', 50).then(setSessions).catch(() => {});
  }, []);

  const loadSession = useCallback(async (sid: string) => {
    try {
      const data = await chatApi.loadSession(sid);
      setSessionId(sid);
      setMessages(
        data.messages.map((m) => ({
          role: m.role,
          content: m.content,
          timestamp: m.created_at?.slice(11, 16) || '',
        })),
      );
    } catch {
      /* ignore */
    }
  }, []);

  const deleteSession = useCallback(
    async (sid: string) => {
      try {
        await chatApi.deleteSession(sid);
      } catch {
        /* ignore */
      }
      if (sessionId === sid) {
        setSessionId(null);
        setMessages([]);
      }
      setSessions((prev) => prev.filter((s) => s.id !== sid));
    },
    [sessionId],
  );

  const newChat = useCallback(() => {
    setSessionId(null);
    setMessages([]);
    setFiles([]);
    if (inputRef.current) inputRef.current.value = '';
  }, []);

  const pickModel = useCallback(
    (value: string) => {
      setModel(value);
      setModelDropOpen(false);
      // Auto-select first OCI config for direct models
      if (value && !value.startsWith('cfg:') && !ociConfig && ociCfg.length) {
        setOciConfig(ociCfg[0].id);
      }
    },
    [ociConfig, ociCfg],
  );

  const handleFiles = useCallback(
    (fileList: FileList | null) => {
      if (!fileList) return;
      const newFiles: AttachedFile[] = [];
      for (const f of Array.from(fileList)) {
        if (files.length + newFiles.length >= 5) break;
        const entry: AttachedFile = { file: f, name: f.name, type: f.type.startsWith('image/') ? 'image' : 'file' };
        if (entry.type === 'image') {
          entry.preview = URL.createObjectURL(f);
        }
        newFiles.push(entry);
      }
      setFiles((prev) => [...prev, ...newFiles].slice(0, 5));
      if (fileInputRef.current) fileInputRef.current.value = '';
    },
    [files.length],
  );

  const removeFile = useCallback((idx: number) => {
    setFiles((prev) => {
      const f = prev[idx];
      if (f.preview) URL.revokeObjectURL(f.preview);
      return prev.filter((_, i) => i !== idx);
    });
  }, []);

  /* ── Send ── */

  const sendMessage = useCallback(async () => {
    const text = inputRef.current?.value.trim() || '';
    const hasFiles = files.length > 0;
    if (!text && !hasFiles) return;
    if (!model) return;

    // Validate OCI config for direct models
    if (isDirect && !ociConfig) {
      setMessages((prev) => [
        ...prev,
        { role: 'assistant', content: t('chat.selectOciCred'), timestamp: nowHHmm(), failed: true },
      ]);
      return;
    }

    setSending(true);
    if (inputRef.current) inputRef.current.value = '';

    // User message display
    let userDisplay = text;
    if (hasFiles) {
      const fileList = files.map((f) => f.name).join(', ');
      userDisplay += (userDisplay ? '\n' : '') + '\u{1F4CE} ' + fileList;
    }

    setMessages((prev) => [
      ...prev,
      { role: 'user', content: userDisplay, timestamp: nowHHmm() },
      { role: 'assistant', content: '', timestamp: '', thinking: true },
    ]);

    try {
      let data: { message_id: string; session_id: string; status: string };

      if (hasFiles) {
        const fd = new FormData();
        fd.append('message', text || t('chat.analyzeFiles'));
        if (sessionId) fd.append('session_id', sessionId);
        fd.append('use_tools', String(useTools));

        if (model.startsWith('cfg:')) {
          fd.append('genai_config_id', model.slice(4));
        } else {
          fd.append('model_id', model);
          fd.append('oci_config_id', ociConfig);
          const oc = ociCfg.find((c) => c.id === ociConfig);
          if (oc) {
            fd.append('genai_region', oc.region);
            fd.append('compartment_id', oc.compartment_id || '');
          }
        }

        fd.append('temperature', String(chatParams.temperature));
        fd.append('max_tokens', String(chatParams.max_tokens));
        fd.append('top_p', String(chatParams.top_p));
        fd.append('top_k', String(chatParams.top_k));
        fd.append('frequency_penalty', String(chatParams.frequency_penalty));
        fd.append('presence_penalty', String(chatParams.presence_penalty));
        if (isReasoning && chatParams.reasoning_effort) {
          fd.append('reasoning_effort', chatParams.reasoning_effort);
        }
        files.forEach((f) => fd.append('files', f.file));

        data = await chatApi.upload(fd);
        setFiles([]);
      } else {
        const body: ChatSendBody = {
          message: text,
          session_id: sessionId || undefined,
          use_tools: useTools,
        };

        if (model.startsWith('cfg:')) {
          body.genai_config_id = model.slice(4);
        } else {
          body.model_id = model;
          body.oci_config_id = ociConfig;
          const oc = ociCfg.find((c) => c.id === ociConfig);
          if (oc) {
            body.genai_region = oc.region;
            body.compartment_id = oc.compartment_id || '';
          }
        }

        body.temperature = chatParams.temperature;
        body.max_tokens = chatParams.max_tokens;
        body.top_p = chatParams.top_p;
        body.top_k = chatParams.top_k;
        body.frequency_penalty = chatParams.frequency_penalty;
        body.presence_penalty = chatParams.presence_penalty;
        if (isReasoning && chatParams.reasoning_effort) {
          body.reasoning_effort = chatParams.reasoning_effort;
        }

        data = await chatApi.send(body);
      }

      setSessionId(data.session_id);

      if (data.status === 'processing' && data.message_id) {
        await pollResult(data.message_id);
      } else {
        setMessages((prev) => prev.filter((m) => !m.thinking));
      }

      refreshSessions();
    } catch (err: any) {
      setMessages((prev) => [
        ...prev.filter((m) => !m.thinking),
        { role: 'assistant', content: `Erro: ${err.message || t('chat.sendError')}`, timestamp: nowHHmm(), failed: true },
      ]);
    } finally {
      setSending(false);
      inputRef.current?.focus();
    }
  }, [model, sessionId, ociConfig, files, chatParams, useTools, isDirect, isReasoning, ociCfg, refreshSessions, t]);

  /* ── Poll ── */

  const pollResult = useCallback(
    async (mid: string) => {
      for (let i = 0; i < 1200; i++) {
        await new Promise<void>((resolve) => {
          pollRef.current = setTimeout(resolve, i < 10 ? 1000 : 3000);
        });
        try {
          const r = await chatApi.pollStatus(mid);
          if (r.status === 'done') {
            setMessages((prev) => [
              ...prev.filter((m) => !m.thinking),
              { role: 'assistant', content: r.content, timestamp: nowHHmm() },
            ]);
            return;
          }
          if (r.status === 'failed') {
            setMessages((prev) => [
              ...prev.filter((m) => !m.thinking),
              { role: 'assistant', content: r.content || t('chat.requestFailed'), timestamp: nowHHmm(), failed: true },
            ]);
            return;
          }
        } catch {
          /* continue polling */
        }
      }
      // Timeout
      setMessages((prev) => [
        ...prev.filter((m) => !m.thinking),
        { role: 'assistant', content: t('chat.timeout'), timestamp: nowHHmm(), failed: true },
      ]);
    },
    [t],
  );

  /* ── Retry ── */

  const retryMessage = useCallback(
    (idx: number) => {
      // Find last user message before this failed one
      let userMsg = '';
      for (let i = idx - 1; i >= 0; i--) {
        if (messages[i].role === 'user') {
          userMsg = messages[i].content;
          break;
        }
      }
      if (!userMsg) return;
      // Remove failed message
      setMessages((prev) => prev.filter((_, i) => i !== idx));
      // Put text back in input and send
      if (inputRef.current) inputRef.current.value = userMsg;
      // Auto-send after state update
      setTimeout(() => sendMessage(), 50);
    },
    [messages, sendMessage],
  );

  /* ── Auto-grow textarea ── */

  const autoGrow = useCallback((el: HTMLTextAreaElement) => {
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 200) + 'px';
  }, []);

  /* ── Render ── */

  return (
    <div className="flex" style={{ height: 'calc(100vh - 49px)', background: 'var(--bg)' }}>
      {/* ── Left Panel: History ── */}
      <div
        className="flex flex-col flex-shrink-0 transition-all duration-200 overflow-hidden"
        style={{
          width: historyOpen ? 260 : 0,
          borderRight: historyOpen ? '1px solid var(--bd)' : 'none',
          background: 'var(--bg1)',
        }}
      >
        <div
          className="flex items-center justify-between px-3 py-2 flex-shrink-0"
          style={{ borderBottom: '1px solid var(--bd)' }}
        >
          <span className="text-xs font-semibold" style={{ color: 'var(--t2)' }}>
            {t('chat.history')}
          </span>
          <button
            onClick={newChat}
            className="flex items-center gap-1 px-2 py-1 rounded text-xs cursor-pointer"
            style={{ background: 'var(--ac)', color: '#fff' }}
          >
            <Plus size={14} /> {t('chat.newChat')}
          </button>
        </div>
        <div className="flex-1 overflow-y-auto px-1 py-2" style={{ fontSize: '0.75rem' }}>
          {Object.entries(sessionGroups).map(([group, items]) => (
            <div key={group} className="mb-2">
              <div
                className="px-2 py-1 text-xs font-semibold uppercase"
                style={{ color: 'var(--t4)', fontSize: '0.62rem', letterSpacing: '0.05em' }}
              >
                {group}
              </div>
              {items.map((s) => (
                <div
                  key={s.id}
                  onClick={() => loadSession(s.id)}
                  className="group flex items-center justify-between px-2 py-1.5 rounded cursor-pointer"
                  style={{
                    background: sessionId === s.id ? 'var(--bg3)' : 'transparent',
                    color: sessionId === s.id ? 'var(--t1)' : 'var(--t3)',
                  }}
                  onMouseEnter={(e) => (e.currentTarget.style.background = sessionId === s.id ? 'var(--bg3)' : 'var(--bg2)')}
                  onMouseLeave={(e) => (e.currentTarget.style.background = sessionId === s.id ? 'var(--bg3)' : 'transparent')}
                >
                  <div className="flex-1 min-w-0">
                    <div className="truncate text-xs">{s.title || t('chat.untitled')}</div>
                    <div className="text-xs" style={{ color: 'var(--t4)', fontSize: '0.6rem' }}>
                      {timeAgo(s.updated_at || s.created_at)}
                    </div>
                  </div>
                  <button
                    onClick={(e) => {
                      e.stopPropagation();
                      deleteSession(s.id);
                    }}
                    className="opacity-0 group-hover:opacity-100 p-0.5 rounded cursor-pointer"
                    style={{ color: 'var(--rd)' }}
                  >
                    <Trash2 size={14} />
                  </button>
                </div>
              ))}
            </div>
          ))}
          {sessions.length === 0 && (
            <div className="px-3 py-4 text-center text-xs" style={{ color: 'var(--t4)' }}>
              {t('chat.noConversations')}
            </div>
          )}
        </div>
      </div>

      {/* ── Center Panel: Chat ── */}
      <div className="flex flex-col flex-1 min-w-0">
        {/* Toolbar */}
        <div
          className="flex items-center gap-2 px-3 py-2 flex-shrink-0 flex-wrap"
          style={{ borderBottom: '1px solid var(--bd)', background: 'var(--bg1)' }}
        >
          {/* History toggle */}
          <button
            onClick={() => {
              setHistoryOpen((v) => !v);
              if (!historyOpen) refreshSessions();
            }}
            className="p-1.5 rounded cursor-pointer"
            style={{ background: historyOpen ? 'var(--bg3)' : 'transparent', color: 'var(--t2)' }}
            title={t('chat.history')}
          >
            <Menu size={18} />
          </button>

          {/* Model dropdown */}
          <div className="relative" ref={modelDropRef}>
            <button
              onClick={() => setModelDropOpen((v) => !v)}
              className="flex items-center gap-1.5 px-2.5 py-1.5 rounded text-xs cursor-pointer"
              style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t2)', maxWidth: 280 }}
            >
              <span className="truncate">{modelLabel}</span>
              <ChevronDown size={14} className="flex-shrink-0" />
            </button>
            {modelDropOpen && (
              <div
                className="absolute left-0 top-full mt-1 rounded-lg overflow-hidden z-50"
                style={{
                  width: 300,
                  background: 'var(--bg1)',
                  border: '1px solid var(--bd)',
                  boxShadow: 'var(--sh1)',
                  maxHeight: 360,
                }}
              >
                <div className="p-2" style={{ borderBottom: '1px solid var(--bd)' }}>
                  <div className="flex items-center gap-2 px-2 py-1 rounded" style={{ background: 'var(--bg2)' }}>
                    <Search size={14} style={{ color: 'var(--t4)' }} />
                    <input
                      type="text"
                      placeholder={t('chat.searchModel')}
                      value={modelSearch}
                      onChange={(e) => setModelSearch(e.target.value)}
                      className="flex-1 bg-transparent text-xs outline-none"
                      style={{ color: 'var(--t2)', border: 'none' }}
                      autoFocus
                    />
                  </div>
                </div>
                <div className="overflow-y-auto" style={{ maxHeight: 300 }}>
                  {modelGroups.map((group) => (
                    <div key={group.label}>
                      <div
                        className="px-3 py-1 text-xs font-semibold uppercase"
                        style={{ color: 'var(--t4)', fontSize: '0.6rem', background: 'var(--bg)', letterSpacing: '0.05em' }}
                      >
                        {group.label}
                      </div>
                      {group.items.map((item) => (
                        <div
                          key={item.value}
                          onClick={() => pickModel(item.value)}
                          className="px-3 py-1.5 text-xs cursor-pointer"
                          style={{
                            color: model === item.value ? 'var(--ac)' : 'var(--t2)',
                            background: model === item.value ? 'var(--bg2)' : 'transparent',
                          }}
                          onMouseEnter={(e) => {
                            if (model !== item.value) e.currentTarget.style.background = 'var(--bg2)';
                          }}
                          onMouseLeave={(e) => {
                            if (model !== item.value) e.currentTarget.style.background = 'transparent';
                          }}
                        >
                          {item.name}
                        </div>
                      ))}
                    </div>
                  ))}
                  {modelGroups.length === 0 && (
                    <div className="px-3 py-3 text-xs text-center" style={{ color: 'var(--t4)' }}>
                      {t('chat.noModels')}
                    </div>
                  )}
                </div>
              </div>
            )}
          </div>

          {/* OCI config selector for direct models */}
          {isDirect && (
            <select
              value={ociConfig}
              onChange={(e) => setOciConfig(e.target.value)}
              className="px-2 py-1.5 rounded text-xs"
              style={{
                background: 'var(--bg2)',
                border: '1px solid var(--bd)',
                color: 'var(--t2)',
                maxWidth: 200,
              }}
            >
              <option value="">{t('chat.ociCred')}</option>
              {ociCfg.map((c) => (
                <option key={c.id} value={c.id}>
                  {c.tenancy_name} ({c.region})
                </option>
              ))}
            </select>
          )}

          {/* RAG badge */}
          {hasRag && (
            <span
              className="px-1.5 py-0.5 rounded text-xs font-semibold"
              style={{ background: 'rgba(16,185,129,0.15)', color: 'var(--gn)', fontSize: '0.62rem' }}
            >
              <Database size={10} className="inline mr-0.5" style={{ verticalAlign: '-1px' }} />
              RAG
            </span>
          )}

          <div className="flex-1" />

          {/* Settings toggle */}
          <button
            onClick={() => setSettingsOpen((v) => !v)}
            className="p-1.5 rounded cursor-pointer"
            style={{ background: settingsOpen ? 'var(--bg3)' : 'transparent', color: 'var(--t2)' }}
            title={t('chat.settings')}
          >
            <Settings size={18} />
          </button>

          {/* New chat */}
          <button
            onClick={newChat}
            className="p-1.5 rounded cursor-pointer"
            style={{ color: 'var(--t2)' }}
            title={t('chat.newChatTitle')}
          >
            <Plus size={18} />
          </button>
        </div>

        {/* Messages area */}
        <div className="flex-1 overflow-y-auto px-4 py-4">
          {messages.length === 0 ? (
            <div className="flex flex-col items-center justify-center h-full gap-3">
              <Bot size={52} style={{ color: 'var(--t4)', opacity: 0.5 }} />
              <p className="text-sm" style={{ color: 'var(--t4)' }}>
                {t('chat.emptyTitle')}
              </p>
              <p className="text-xs" style={{ color: 'var(--t4)', opacity: 0.7 }}>
                {t('chat.emptySubtitle')}
              </p>
            </div>
          ) : (
            <div className="flex flex-col gap-3 max-w-3xl mx-auto">
              {messages.map((msg, idx) => {
                if (msg.thinking) {
                  return (
                    <div key={idx} className="flex items-center gap-2 self-start px-4 py-3 rounded-xl" style={{ background: 'var(--bg1)' }}>
                      <Loader2 size={16} className="animate-spin" style={{ color: 'var(--ac)' }} />
                      <span className="text-sm italic" style={{ color: 'var(--t3)' }}>
                        {t('chat.thinking')}
                      </span>
                    </div>
                  );
                }

                const isUser = msg.role === 'user';

                return (
                  <div
                    key={idx}
                    className={`flex flex-col ${isUser ? 'items-end' : 'items-start'}`}
                  >
                    <div
                      className="px-4 py-2.5 rounded-xl text-sm max-w-full"
                      style={{
                        background: isUser
                          ? 'linear-gradient(135deg, var(--ac), var(--ach))'
                          : 'var(--bg1)',
                        color: isUser ? '#fff' : 'var(--t2)',
                        border: msg.failed ? '1px solid var(--rd)' : isUser ? 'none' : '1px solid var(--bd)',
                        maxWidth: '85%',
                        wordBreak: 'break-word',
                      }}
                    >
                      {isUser ? (
                        <div style={{ whiteSpace: 'pre-wrap' }}>{msg.content}</div>
                      ) : (
                        <div className="chat-markdown prose prose-sm max-w-none [&_pre]:overflow-x-auto [&_table]:overflow-x-auto" style={{ overflowWrap: 'break-word', wordBreak: 'break-word' }}>
                          <ReactMarkdown remarkPlugins={[remarkGfm]}>{msg.content}</ReactMarkdown>
                        </div>
                      )}
                    </div>
                    <div className="flex items-center gap-2 mt-0.5 px-1">
                      {msg.timestamp && (
                        <span className="text-xs" style={{ color: 'var(--t4)', fontSize: '0.62rem' }}>
                          {msg.timestamp}
                        </span>
                      )}
                      {msg.failed && (
                        <button
                          onClick={() => retryMessage(idx)}
                          className="flex items-center gap-1 text-xs cursor-pointer px-1.5 py-0.5 rounded"
                          style={{ color: 'var(--rd)', fontSize: '0.65rem' }}
                        >
                          <RefreshCw size={12} /> {t('chat.retry')}
                        </button>
                      )}
                    </div>
                  </div>
                );
              })}
              <div ref={messagesEndRef} />
            </div>
          )}
        </div>

        {/* File preview chips */}
        {files.length > 0 && (
          <div
            className="flex items-center gap-2 flex-wrap px-4 py-2"
            style={{ borderTop: '1px solid var(--bd)', background: 'var(--bg1)' }}
          >
            {files.map((f, i) =>
              f.type === 'image' && f.preview ? (
                <div key={i} className="relative" style={{ width: 48, height: 48 }}>
                  <img
                    src={f.preview}
                    alt={f.name}
                    className="rounded"
                    style={{ width: 48, height: 48, objectFit: 'cover', border: '1px solid var(--bd)' }}
                  />
                  <button
                    onClick={() => removeFile(i)}
                    className="absolute flex items-center justify-center rounded-full cursor-pointer"
                    style={{
                      top: -4,
                      right: -4,
                      width: 16,
                      height: 16,
                      background: 'var(--rd)',
                      color: '#fff',
                      fontSize: '0.55rem',
                    }}
                  >
                    <X size={10} />
                  </button>
                </div>
              ) : (
                <div
                  key={i}
                  className="flex items-center gap-1.5 px-2 py-1 rounded text-xs"
                  style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t3)' }}
                >
                  <Paperclip size={12} />
                  <span className="truncate" style={{ maxWidth: 120 }}>
                    {f.name}
                  </span>
                  <button onClick={() => removeFile(i)} className="cursor-pointer" style={{ color: 'var(--rd)' }}>
                    <X size={12} />
                  </button>
                </div>
              ),
            )}
          </div>
        )}

        {/* Input area */}
        <div
          className="flex items-end gap-2 px-4 py-3 flex-shrink-0"
          style={{ borderTop: '1px solid var(--bd)', background: 'var(--bg1)' }}
        >
          <input
            ref={fileInputRef}
            type="file"
            multiple
            accept={ACCEPT}
            className="hidden"
            onChange={(e) => handleFiles(e.target.files)}
          />
          <button
            onClick={() => fileInputRef.current?.click()}
            className="p-2 rounded cursor-pointer flex-shrink-0"
            style={{ color: 'var(--t3)', background: 'var(--bg2)', border: '1px solid var(--bd)' }}
            title={t('chat.attachFile')}
          >
            <Paperclip size={18} />
          </button>
          <textarea
            ref={inputRef}
            placeholder={t('chat.placeholder')}
            rows={1}
            disabled={sending}
            onKeyDown={(e) => {
              if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                sendMessage();
              }
            }}
            onInput={(e) => autoGrow(e.currentTarget)}
            className="flex-1 px-3 py-2 rounded-lg text-sm outline-none"
            style={{
              background: 'var(--bg2)',
              border: '1px solid var(--bd)',
              color: 'var(--t2)',
              resize: 'none',
              overflow: 'hidden',
              maxHeight: 200,
              fontFamily: 'var(--fm)',
            }}
          />
          <button
            onClick={sendMessage}
            disabled={sending}
            className="p-2 rounded cursor-pointer flex-shrink-0"
            style={{
              background: sending ? 'var(--bg3)' : 'var(--ac)',
              color: '#fff',
              opacity: sending ? 0.6 : 1,
            }}
            title={t('chat.send')}
          >
            {sending ? <Loader2 size={18} className="animate-spin" /> : <Send size={18} />}
          </button>
        </div>
      </div>

      {/* ── Right Panel: Settings ── */}
      <div
        className="flex flex-col flex-shrink-0 transition-all duration-200 overflow-hidden"
        style={{
          width: settingsOpen ? 280 : 0,
          borderLeft: settingsOpen ? '1px solid var(--bd)' : 'none',
          background: 'var(--bg1)',
        }}
      >
        <div
          className="flex items-center gap-2 px-3 py-2 flex-shrink-0"
          style={{ borderBottom: '1px solid var(--bd)' }}
        >
          <Settings size={16} style={{ color: 'var(--t3)' }} />
          <span className="text-xs font-semibold" style={{ color: 'var(--t2)' }}>
            {t('chat.settings')}
          </span>
        </div>
        <div className="flex-1 overflow-y-auto px-3 py-3">
          <div className="flex flex-col gap-3">
            {/* Reasoning models: only max_tokens + reasoning_effort */}
            {isReasoning ? (
              <>
                <SettingField label={`Max Tokens (max ${maxTokenLimit.toLocaleString()})`}>
                  <input
                    type="number"
                    value={chatParams.max_tokens}
                    min={1}
                    max={maxTokenLimit}
                    onChange={(e) => setChatParams((p) => ({ ...p, max_tokens: +e.target.value }))}
                    className="w-full px-2 py-1.5 rounded text-xs"
                    style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t2)' }}
                  />
                </SettingField>
                <SettingField label="Reasoning Effort">
                  <select
                    value={chatParams.reasoning_effort}
                    onChange={(e) => setChatParams((p) => ({ ...p, reasoning_effort: e.target.value }))}
                    className="w-full px-2 py-1.5 rounded text-xs"
                    style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t2)' }}
                  >
                    <option value="low">Low</option>
                    <option value="medium">Medium</option>
                    <option value="high">High</option>
                  </select>
                </SettingField>
              </>
            ) : (
              <>
                <SettingField label="Temperature">
                  <div className="flex items-center gap-2">
                    <input
                      type="range"
                      min={0}
                      max={2}
                      step={0.1}
                      value={chatParams.temperature}
                      onChange={(e) => setChatParams((p) => ({ ...p, temperature: +e.target.value }))}
                      className="flex-1"
                      style={{ accentColor: 'var(--ac)' }}
                    />
                    <span className="text-xs w-8 text-right" style={{ color: 'var(--t3)' }}>
                      {chatParams.temperature.toFixed(1)}
                    </span>
                  </div>
                </SettingField>
                <SettingField label={`Max Tokens (max ${maxTokenLimit.toLocaleString()})`}>
                  <input
                    type="number"
                    value={chatParams.max_tokens}
                    min={1}
                    max={maxTokenLimit}
                    onChange={(e) => setChatParams((p) => ({ ...p, max_tokens: +e.target.value }))}
                    className="w-full px-2 py-1.5 rounded text-xs"
                    style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t2)' }}
                  />
                </SettingField>
                <SettingField label="Top P">
                  <div className="flex items-center gap-2">
                    <input
                      type="range"
                      min={0}
                      max={1}
                      step={0.05}
                      value={chatParams.top_p}
                      onChange={(e) => setChatParams((p) => ({ ...p, top_p: +e.target.value }))}
                      className="flex-1"
                      style={{ accentColor: 'var(--ac)' }}
                    />
                    <span className="text-xs w-8 text-right" style={{ color: 'var(--t3)' }}>
                      {chatParams.top_p.toFixed(2)}
                    </span>
                  </div>
                </SettingField>
                {hasTopK && (
                  <SettingField label="Top K">
                    <input
                      type="number"
                      value={chatParams.top_k}
                      min={-1}
                      max={500}
                      onChange={(e) => setChatParams((p) => ({ ...p, top_k: +e.target.value }))}
                      className="w-full px-2 py-1.5 rounded text-xs"
                      style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t2)' }}
                    />
                  </SettingField>
                )}
                {hasPenalties && (
                  <>
                    <SettingField label="Frequency Penalty">
                      <div className="flex items-center gap-2">
                        <input
                          type="range"
                          min={-2}
                          max={2}
                          step={0.1}
                          value={chatParams.frequency_penalty}
                          onChange={(e) => setChatParams((p) => ({ ...p, frequency_penalty: +e.target.value }))}
                          className="flex-1"
                          style={{ accentColor: 'var(--ac)' }}
                        />
                        <span className="text-xs w-8 text-right" style={{ color: 'var(--t3)' }}>
                          {chatParams.frequency_penalty.toFixed(1)}
                        </span>
                      </div>
                    </SettingField>
                    <SettingField label="Presence Penalty">
                      <div className="flex items-center gap-2">
                        <input
                          type="range"
                          min={-2}
                          max={2}
                          step={0.1}
                          value={chatParams.presence_penalty}
                          onChange={(e) => setChatParams((p) => ({ ...p, presence_penalty: +e.target.value }))}
                          className="flex-1"
                          style={{ accentColor: 'var(--ac)' }}
                        />
                        <span className="text-xs w-8 text-right" style={{ color: 'var(--t3)' }}>
                          {chatParams.presence_penalty.toFixed(1)}
                        </span>
                      </div>
                    </SettingField>
                  </>
                )}
              </>
            )}

            {/* MCP Tools */}
            <div className="pt-3 mt-1" style={{ borderTop: '1px solid var(--bd)' }}>
              <label
                className="flex items-center gap-2 cursor-pointer text-xs"
                style={{ color: 'var(--t2)' }}
              >
                <input
                  type="checkbox"
                  checked={useTools}
                  onChange={(e) => setUseTools(e.target.checked)}
                  style={{ accentColor: 'var(--ac)' }}
                />
                <Wrench size={14} />
                MCP Tools
              </label>
              {toolCount > 0 && (
                <span className="block mt-1 text-xs" style={{ color: 'var(--t4)', fontSize: '0.66rem' }}>
                  {toolCount} {t('chat.toolsAvailable')}
                </span>
              )}
            </div>

            {/* OCI config in settings for direct models */}
            {isDirect && (
              <div className="pt-3 mt-1" style={{ borderTop: '1px solid var(--bd)' }}>
                <SettingField label={t('chat.ociCredLabel')}>
                  <select
                    value={ociConfig}
                    onChange={(e) => setOciConfig(e.target.value)}
                    className="w-full px-2 py-1.5 rounded text-xs"
                    style={{ background: 'var(--bg2)', border: '1px solid var(--bd)', color: 'var(--t2)' }}
                  >
                    <option value="">{t('chat.select')}</option>
                    {ociCfg.map((c) => (
                      <option key={c.id} value={c.id}>
                        {c.tenancy_name} ({c.region})
                      </option>
                    ))}
                  </select>
                </SettingField>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ── Tiny sub-component ── */

function SettingField({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div>
      <label className="block text-xs mb-1" style={{ color: 'var(--t3)', fontSize: '0.7rem' }}>
        {label}
      </label>
      {children}
    </div>
  );
}
