const Supervisor = () => {
  const { Icon, Button, useToast, Empty } = window.SB_UI;
  const { fa } = window.SB_DATA;
  const toast = useToast();

  const [items, setItems]       = useState([]);
  const [counts, setCounts]     = useState({});
  const [status, setStatus]     = useState('pending');
  const [loading, setLoading]   = useState(false);
  const [labeling, setLabeling] = useState(false);
  const [retraining, setRetraining] = useState(false);
  const [intents, setIntents]   = useState([]);
  const [apiKey, setApiKey]     = useState('');
  const [model, setModel]       = useState('gemini-1.5-flash');
  const [editItem, setEditItem] = useState(null);
  const [editIntent, setEditIntent] = useState('');

  // چت تست
  const [chatHistory, setChatHistory] = useState([]);
  const [chatInput, setChatInput]     = useState('');
  const [chatLoading, setChatLoading] = useState(false);
  const chatEndRef    = useRef(null);
  const chatScrollRef = useRef(null);

  useEffect(() => {
    loadConfig();
    loadIntents();
    loadQueue();
  }, [status]);

  // نشست تست با باز/ریلود شدن صفحه پاک نمی‌شود تا چت برای بررسیِ بعدی بماند.
  // شروعِ تمیز فقط با دکمه‌ی «شروع تازه» (resetChat) انجام می‌شود.

  const loadConfig = () =>
    window.api('/mlmetamarket/supervisor/config').then(r => {
      if (r.success) { setApiKey(r.config.api_key || ''); setModel(r.config.model || 'gemini-1.5-flash'); }
    });

  const loadIntents = () =>
    window.api('/mlmetamarket/learning/intents').then(r => {
      if (r.success) setIntents((r.intents || []).map(x => x.intent));
    });

  const loadQueue = () => {
    setLoading(true);
    window.api(`/mlmetamarket/supervisor/queue?status=${status}&limit=100`)
      .then(r => { if (r.success) { setItems(r.items || []); setCounts(r.counts || {}); } })
      .finally(() => setLoading(false));
  };

  const saveConfig = async () => {
    const r = await window.api('/mlmetamarket/supervisor/config', { method: 'POST', body: { api_key: apiKey, model } });
    if (r.success) toast('تنظیمات ذخیره شد', 'success'); else toast(r.error, 'error');
  };

  // درخواست AI از سرور عبور می‌کند تا کلید در مرورگر افشا نشود و CORS مانع نشود.
  const geminiGenerate = async (text, generationConfig) => {
    if (!apiKey) throw new Error('no-key');
    const serverResult = await window.api('/mlmetamarket/supervisor/ai-generate', {
      method: 'POST',
      body: { prompt: text, generationConfig },
    });
    if (!serverResult.success) throw new Error(serverResult.error || 'AI unavailable');
    return String(serverResult.text || '').trim();
  };

  // برای یک آیتم — از مرورگر Gemini صدا می‌زنه. خروجی: { intent, raw }
  const aiLabel = async (item) => {
    if (!apiKey) { toast('ابتدا API Key را وارد کنید', 'error'); return { intent: null, raw: '' }; }
    const prompt = `تو دسته‌بند intent برای یک ربات فروش فارسی هستی.
پیام کاربر: "${item.text}"

یکی از این intentها را که بهترین تطابق را دارد، عیناً و فقط همان کلمه بنویس (بدون توضیح):
${intents.join('\n')}

اگر پیام نامفهوم بود یا به هیچ‌کدام نمی‌خورد، فقط بنویس: none`;
    const out = (await geminiGenerate(prompt, { temperature: 0, maxOutputTokens: 20 })).trim();
    const raw = out.toLowerCase().replace(/[^a-z_]/g, '');
    // تطبیق دقیق، بعد تطبیق نرم (intent داخل جواب آمده باشد)
    let intent = intents.includes(raw) ? raw : (intents.find(i => raw.includes(i)) || null);
    return { intent, raw: out };
  };

  // برچسب‌گذاری همه موارد pending با AI
  const labelAll = async () => {
    if (!apiKey) { toast('ابتدا API Key را وارد و ذخیره کنید', 'error'); return; }
    const pending = items.filter(x => !x.ai_suggested_intent);
    if (!pending.length) { toast('همه موارد قبلاً برچسب دارند', 'info'); return; }
    setLabeling(true);
    let done = 0;
    for (const item of pending) {
      try {
        const { intent } = await aiLabel(item);
        if (intent) {
          await window.api('/mlmetamarket/supervisor/ai-suggest', { method: 'POST', body: { id: item.id, intent } });
          setItems(prev => prev.map(x => x.id === item.id ? { ...x, ai_suggested_intent: intent } : x));
          done++;
        }
      } catch (e) { console.error(e); }
    }
    setLabeling(false);
    toast(`${fa(done)} پیشنهاد AI ذخیره شد`, 'success');
  };

  const approve = async (item, intentOverride) => {
    const intent = intentOverride || item.ai_suggested_intent;
    if (!intent) { toast('ابتدا AI را اجرا کنید یا intent را انتخاب کنید', 'error'); return; }
    const r = await window.api('/mlmetamarket/supervisor/approve', { method: 'POST', body: { id: item.id, intent } });
    if (r.success) {
      setItems(prev => prev.filter(x => x.id !== item.id));
      setCounts(p => ({ ...p, pending: Math.max(0, (p.pending || 1) - 1), approved: (p.approved || 0) + 1 }));
      toast(`تأیید شد → ${intent}`, 'success');
    } else toast(r.error, 'error');
    setEditItem(null);
  };

  const reject = async (id) => {
    const r = await window.api('/mlmetamarket/supervisor/reject', { method: 'POST', body: { id } });
    if (r.success) {
      setItems(prev => prev.filter(x => x.id !== id));
      setCounts(p => ({ ...p, pending: Math.max(0, (p.pending || 1) - 1), rejected: (p.rejected || 0) + 1 }));
    } else toast(r.error, 'error');
  };

  const retrain = async () => {
    setRetraining(true);
    const r = await window.api('/mlmetamarket/supervisor/retrain', { method: 'POST' });
    setRetraining(false);
    if (r.success) toast('مدل retrain شد ✅', 'success'); else toast(r.error, 'error');
  };

  const confColor = (n) => n >= 70 ? '#34D399' : n >= 45 ? '#FBBF24' : '#F87171';

  // صدا زدن Gemini مستقیم از مرورگر (از طریق VPN) — وقتی AI سروری در دسترس نیست
  const callGeminiBrowser = async (systemPrompt, userText) =>
    geminiGenerate(`${systemPrompt}\n\nپیام کاربر: ${userText}`, { temperature: 0.6, maxOutputTokens: 250 });

  const sendChat = async () => {
    const text = chatInput.trim();
    if (!text || chatLoading) return;

    // دستور «s» — پاسخ آخر را دوباره از استاد بگیر و وریفای کن
    if (text === 's') {
      const lastBot = [...chatHistory].reverse().find(m => m.role === 'assistant' && m.userText);
      if (!lastBot) { toast('پیام قبلی برای re-verify پیدا نشد', 'error'); return; }
      setChatInput('');
      setChatLoading(true);
      try {
        const r = await window.api('/mlmetamarket/supervisor/re-verify', {
          method: 'POST',
          body: { userText: lastBot.userText, sessionId: 'supervisor-trace' },
        });
        if (r.success) {
          const clearance = r.clearance || null;
          const held = clearance && !clearance.released;
          const reply = held ? '🛡️ حراست: پاسخ جدید هم مجوز نگرفت' : (r.reply ?? '(سکوت)');
          // آخرین پیام assistant را در chatHistory جایگزین کن
          setChatHistory(h => {
            const idx = [...h].reverse().findIndex(m => m.role === 'assistant' && m.userText === lastBot.userText);
            if (idx < 0) return h;
            const realIdx = h.length - 1 - idx;
            const updated = [...h];
            updated[realIdx] = { ...updated[realIdx], text: reply, trace: r.trace || [], source: r.source,
              intent: r.intent, confidence: r.confidence, clearance, heldReply: r.heldReply, reVerified: true };
            return updated;
          });
          setOpenReports(p => ({ ...p })); // force re-render
          toast(held ? 'استاد هم توقف داد 🛑' : '✅ پاسخ جدید از استاد وریفای شد', held ? 'error' : 'success');
        } else toast(r.error, 'error');
      } catch { toast('خطای شبکه', 'error'); }
      setChatLoading(false);
      return;
    }

    setChatHistory(h => [...h, { role: 'user', text }]);
    setChatInput('');
    setChatLoading(true);
    try {
      const r = await window.api('/mlmetamarket/supervisor/trace-chat', {
        method: 'POST',
        body: { message: text, sessionId: 'supervisor-trace' },
      });
      if (r.success) {
        const clearance = r.clearance || null;
        const held = clearance && !clearance.released;
        // پنل باید دقیقاً همون چیزی رو نشون بده که مشتریِ واقعی می‌بینه: وقتی حراست
        // پیام رو بلاک می‌کنه، مشتری هیچی نمی‌بینه (سکوت) — نه یه متنِ فنی. جزئیاتِ
        // فنیِ چرا بلاک شد همچنان تویِ بلوکِ Clearance زیرِ پیام نشون داده می‌شه.
        let reply = held ? '(سکوت — این پیام برایِ مشتری ارسال نشد)' : (r.reply ?? '(سکوت — پاسخی ارسال نشد)');
        let source = r.source;
        let trace = r.trace || [];

        // پیامِ متوقف‌شده هرگز به مشتری نمی‌رود → دیگر سراغِ Gemini مرورگری هم نرو
        if (held) {
          setChatHistory(h => [...h, {
            role: 'assistant', text: reply, trace, source: 'held',
            intent: r.intent, confidence: r.confidence, clearance, heldReply: r.heldReply,
            userText: text,
          }]);
          setChatLoading(false);
          setTimeout(() => { if (chatScrollRef.current) chatScrollRef.current.scrollTop = chatScrollRef.current.scrollHeight; }, 50);
          return;
        }

        // اگر off-topic بود و AI سروری نتوانست → Gemini را از مرورگر صدا بزن
        if (r.needsBrowserAi) {
          if (!apiKey) {
            reply = '⚠️ این پیام نیاز به AI دارد ولی هیچ AI در دسترس نیست. در «تنظیمات» کلید Gemini را وارد کنید.';
            source = 'no_ai';
          } else {
            try {
              const g = await callGeminiBrowser(r.browserPrompt, r.userText || text);
              if (g) {
                reply = g;
                source = 'ai_browser';
                trace = trace.map(s => ({ ...s, active: false }));
                // مرحله موتور را با Gemini مرورگر جایگزین/علامت بزن
                const engIdx = trace.findIndex(s => s.stage === 'engine');
                const gStep = { stage: 'engine', icon: '🌐', title: 'موتور: Gemini (مرورگر + VPN)',
                  detail: 'ML مطمئن نبود → Gemini مستقیم از مرورگر جواب داد', active: true };
                if (engIdx >= 0) trace[engIdx] = gStep; else trace.push(gStep);
              }
            } catch (e) {
              reply = `⚠️ Gemini از مرورگر جواب نداد: ${e.message}`;
              source = 'no_ai';
            }
          }
        }

        setChatHistory(h => [...h, {
          role: 'assistant', text: reply, trace,
          source, intent: r.intent, confidence: r.confidence,
          learnable: r.learnable, userText: text, clearance,
        }]);
      } else {
        setChatHistory(h => [...h, { role: 'assistant', text: '❌ ' + r.error }]);
      }
    } catch (e) {
      setChatHistory(h => [...h, { role: 'assistant', text: '❌ خطای شبکه' }]);
    }
    setChatLoading(false);
    setTimeout(() => {
      if (chatScrollRef.current) chatScrollRef.current.scrollTop = chatScrollRef.current.scrollHeight;
    }, 50);
  };

  const resetChat = async () => {
    await window.api('/mlmetamarket/supervisor/trace-reset', { method: 'POST', body: { sessionId: 'supervisor-trace' } });
    setChatHistory([]);
    toast('مکالمه تست از نو شروع شد', 'success');
  };

  // بررسی دستی یک پیام با AI — AI intent درست را تعیین و به ML یاد می‌دهد
  const reviewOne = async (userText, msgIdx) => {
    setChatHistory(h => h.map((m, i) => i === msgIdx ? { ...m, reviewing: true } : m));
    const r = await window.api('/mlmetamarket/supervisor/ai-review-one', { method: 'POST', body: { text: userText } });
    if (r.success) {
      if (r.learned) toast(`✓ AI گفت «${r.correct}» — ML یاد گرفت`, 'success');
      else toast(`AI گفت: ${r.correct} (یاد نگرفت)`, 'info');
      setChatHistory(h => h.map((m, i) => i === msgIdx ? { ...m, reviewing: false, reviewed: r.correct, learned_now: r.learned ? r.correct : null } : m));
      return;
    }
    // AI سرور در دسترس نیست → Gemini از مرورگر
    if (!apiKey) {
      toast('AI سرور در دسترس نیست — کلید Gemini را در تنظیمات وارد کنید', 'error');
      setChatHistory(h => h.map((m, i) => i === msgIdx ? { ...m, reviewing: false } : m));
      return;
    }
    try {
      const { intent: correct, raw } = await aiLabel({ text: userText });
      if (!correct) {
        toast(`Gemini به intent خاصی نرسید (گفت: «${raw || '—'}») — احتمالاً پیام نامفهوم بود`, 'info');
        setChatHistory(h => h.map((m, i) => i === msgIdx ? { ...m, reviewing: false } : m)); return;
      }
      const learnR = await window.api('/mlmetamarket/supervisor/quick-learn', { method: 'POST', body: { text: userText, intent: correct } });
      const learned = learnR.success;
      if (learned) toast(`✓ Gemini (مرورگر) گفت «${correct}» — ML یاد گرفت`, 'success');
      else toast(`Gemini گفت: ${correct}`, 'info');
      setChatHistory(h => h.map((m, i) => i === msgIdx ? { ...m, reviewing: false, reviewed: correct, learned_now: learned ? correct : null } : m));
    } catch (e) {
      toast(`Gemini خطا داد: ${e.message}`, 'error');
      setChatHistory(h => h.map((m, i) => i === msgIdx ? { ...m, reviewing: false } : m));
    }
  };

  // بلوک نمایش پرامت + جواب خام استاد
  const MasterDebugBlock = ({ prompt, raw, label }) => {
    const [open, setOpen] = useState(false);
    if (!prompt) return null;
    return (
      <div style={{ marginTop: 8, border: '1px solid rgba(56,189,248,.25)', borderRadius: 8, overflow: 'hidden' }}>
        <div onClick={() => setOpen(p => !p)} style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 10px', cursor: 'pointer', background: 'rgba(56,189,248,.07)' }}>
          <span style={{ fontSize: 11, fontWeight: 700, color: '#38BDF8' }}>📡 {label || 'پرامت استاد'}</span>
          <span style={{ fontSize: 10, color: 'var(--text-3)', marginRight: 'auto' }}>{open ? '▲ بستن' : '▼ نمایش'}</span>
        </div>
        {open && (
          <div style={{ padding: '8px 10px', display: 'flex', flexDirection: 'column', gap: 8 }}>
            <div>
              <div style={{ fontSize: 10, fontWeight: 700, color: '#38BDF8', marginBottom: 4 }}>پرامت ارسال‌شده به استاد:</div>
              <pre style={{ fontSize: 10, color: 'var(--text-3)', whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'var(--bg)', borderRadius: 6, padding: '6px 8px', maxHeight: 220, overflowY: 'auto', margin: 0 }}>{prompt}</pre>
            </div>
            {raw && (
              <div>
                <div style={{ fontSize: 10, fontWeight: 700, color: '#4ADE80', marginBottom: 4 }}>جواب خام استاد:</div>
                <pre style={{ fontSize: 10.5, color: '#4ADE80', whiteSpace: 'pre-wrap', wordBreak: 'break-word', background: 'rgba(74,222,128,.06)', borderRadius: 6, padding: '6px 8px', margin: 0 }}>{raw}</pre>
              </div>
            )}
          </div>
        )}
      </div>
    );
  };

  // گزارش کامل — کدام پیام‌ها باز هستند
  const [openReports, setOpenReports] = useState({});
  const toggleReport = (idx) => setOpenReports(p => ({ ...p, [idx]: !p[idx] }));

  // پرامت استاد — نمایش مستقیم بدون گزارش کامل
  const [openMasterPrompts, setOpenMasterPrompts] = useState({});
  const toggleMasterPrompt = (idx) => setOpenMasterPrompts(p => ({ ...p, [idx]: !p[idx] }));
  const getMasterDebug = (msg) => {
    const bm = (msg.trace || []).find(s => s.stage === 'board')?.meta || {};
    return bm.masterDebug || msg.clearance?.masterDebug || null;
  };

  const FullReport = ({ msg, idx }) => {
    if (!openReports[idx]) return null;
    const trace = msg.trace || [];
    const routeStage  = trace.find(s => s.stage === 'route');
    const stateStage  = trace.find(s => s.stage === 'state');
    const boardStage  = trace.find(s => s.stage === 'board');
    const dataStage   = trace.find(s => s.stage === 'data');
    const engineStage = trace.find(s => s.stage === 'engine');
    const bm = boardStage?.meta || {};
    const em = engineStage?.meta || {};

    const Row = ({ label, value, color }) => (
      <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start', padding: '4px 0', borderBottom: '1px solid var(--border)' }}>
        <span style={{ fontSize: 11, color: 'var(--text-3)', minWidth: 110, flexShrink: 0 }}>{label}</span>
        <span style={{ fontSize: 11.5, color: color || 'var(--text-2)', wordBreak: 'break-word' }}>{value || '—'}</span>
      </div>
    );
    const verdictColor = (stuck) => stuck ? '#F87171' : '#34D399';
    const verdictIcon  = (stuck) => stuck ? '⚠️' : '✅';

    return (
      <div style={{ marginTop: 8, background: 'var(--bg)', border: '1px solid rgba(99,102,241,.25)', borderRadius: 10, padding: '12px 14px', fontSize: 12 }}>

        {/* اسکریپت + استیت */}
        <div style={{ fontWeight: 800, color: '#A5B4FC', marginBottom: 8, fontSize: 12 }}>📜 اسکریپت و استیت</div>
        <Row label="اسکریپت فعال" value={stateStage?.meta?.scriptName || routeStage?.meta?.scriptName || (routeStage ? 'مغز اصلی' : null)} />
        <Row label="استیت فعلی"   value={stateStage?.meta?.state} />
        <Row label="مرحله"        value={stateStage?.meta?.step != null ? `${fa(stateStage.meta.step)}` : null} />

        {/* نقشه استیت‌های اسکریپت */}
        {stateStage?.meta?.allStates?.length > 0 && (
          <div style={{ marginTop: 8 }}>
            {stateStage.meta.allStates.map((s) => {
              const isDone    = s.status === 'done';
              const isCurrent = s.status === 'current';
              const isPending = s.status === 'pending';
              const color = isDone ? '#34D399' : isCurrent ? '#FBBF24' : '#475569';
              const icon  = isDone ? '✅' : isCurrent ? '▶️' : '⬜';
              return (
                <div key={s.id} style={{ display: 'flex', gap: 6, alignItems: 'flex-start', padding: '3px 0',
                  borderBottom: '1px solid var(--border)', opacity: isPending ? 0.5 : 1 }}>
                  <span style={{ fontSize: 11, minWidth: 20, flexShrink: 0 }}>{icon}</span>
                  <span style={{ fontSize: 10, color: '#94a3b8', minWidth: 22, flexShrink: 0 }}>{fa(s.order)}</span>
                  <span style={{ fontSize: 11, color, fontWeight: isCurrent ? 700 : 400, flex: 1 }}>{s.name}</span>
                  {isCurrent && <span style={{ fontSize: 9, background: 'rgba(251,191,36,.15)', color: '#FBBF24', borderRadius: 4, padding: '1px 5px', flexShrink: 0 }}>الان اینجا</span>}
                  {isDone    && <span style={{ fontSize: 9, background: 'rgba(52,211,153,.12)', color: '#34D399', borderRadius: 4, padding: '1px 5px', flexShrink: 0 }}>رد شد</span>}
                </div>
              );
            })}
          </div>
        )}

        {/* امامام */}
        <div style={{ fontWeight: 800, color: '#818CF8', margin: '10px 0 8px', fontSize: 12 }}>🤖 امامام (ML)</div>
        <Row label="intent"      value={em.intent || msg.intent} />
        <Row label="اطمینان"     value={em.confidence != null ? `${fa(em.confidence)}٪` : (msg.confidence != null ? `${fa(msg.confidence)}٪` : null)} color={em.confidence >= 70 ? '#34D399' : em.confidence >= 45 ? '#FBBF24' : '#F87171'} />
        <Row label="مدل"         value={em.model} />
        <Row label="موتور"       value={em.engine} />

        {/* هیئت معلمین */}
        {boardStage && (<>
          <div style={{ fontWeight: 800, color: '#FBBF24', margin: '10px 0 8px', fontSize: 12 }}>👨‍🏫 هیئت معلمین</div>
          {bm.spelling && (
            <div style={{ display: 'flex', gap: 6, padding: '4px 0', borderBottom: '1px solid var(--border)', alignItems: 'flex-start' }}>
              <span style={{ fontSize: 11, minWidth: 110, color: 'var(--text-3)' }}>{verdictIcon(bm.spelling.stuck)} معلم املا</span>
              <span style={{ fontSize: 11.5, color: verdictColor(bm.spelling.stuck) }}>{bm.spelling.note || 'تایپ درست'}{bm.spelling.fixedTypo ? ' (اصلاح شد)' : ''}</span>
            </div>
          )}
          {bm.analysis && (
            <div style={{ display: 'flex', gap: 6, padding: '4px 0', borderBottom: '1px solid var(--border)', alignItems: 'flex-start' }}>
              <span style={{ fontSize: 11, minWidth: 110, color: 'var(--text-3)' }}>{verdictIcon(bm.analysis.stuck)} معلم تحلیل</span>
              <span style={{ fontSize: 11.5, color: verdictColor(bm.analysis.stuck) }}>{bm.analysis.note || 'تحلیل عادی'} — intent: {bm.analysis.intent} ({fa(bm.analysis.confidence)}٪)</span>
            </div>
          )}
          {bm.transition && (() => {
            const turns = bm.transition.turnsInState ?? 0;
            const lockLevel = turns >= 6 ? 'hard' : turns >= 3 ? 'soft' : 'none';
            const lockBadge = lockLevel === 'hard'
              ? { label: `قفل سخت (${fa(turns)} نوبت)`, bg: 'rgba(239,68,68,.2)', color: '#F87171' }
              : lockLevel === 'soft'
              ? { label: `قفل نرم (${fa(turns)} نوبت)`, bg: 'rgba(251,191,36,.15)', color: '#FBBF24' }
              : turns > 0 ? { label: `${fa(turns)} نوبت در استیت`, bg: 'rgba(148,163,184,.1)', color: '#94a3b8' }
              : null;
            return (
              <div style={{ display: 'flex', gap: 6, padding: '4px 0', borderBottom: '1px solid var(--border)', alignItems: 'flex-start' }}>
                <span style={{ fontSize: 11, minWidth: 110, color: 'var(--text-3)', flexShrink: 0 }}>{verdictIcon(bm.transition.stuck)} معلم عبور</span>
                <span style={{ fontSize: 11.5, color: verdictColor(bm.transition.stuck), flex: 1 }}>{bm.transition.note} ({bm.transition.tentative})</span>
                {lockBadge && (
                  <span style={{ fontSize: 9.5, padding: '2px 7px', borderRadius: 6, background: lockBadge.bg, color: lockBadge.color, flexShrink: 0, fontWeight: 700 }}>
                    {lockBadge.label}
                  </span>
                )}
              </div>
            );
          })()}
          <Row label="استاد (Gemini)"
            value={bm.learnedFromVerified ? 'از حافظه recall شد (بدون تماس)' : bm.masterUnavailable ? 'در دسترس نبود' : bm.masterCalled ? 'صدا زده شد' : 'نیازی نبود'}
            color={bm.masterCalled ? '#38BDF8' : bm.masterUnavailable ? '#F87171' : '#94a3b8'} />
          {bm.boardIntent && <Row label="تصمیم هیئت" value={`${bm.boardIntent} (${bm.boardCategory || '?'})`} color="#C084FC" />}
          {bm.masterDebug?.prompt && (
            <MasterDebugBlock prompt={bm.masterDebug.prompt} raw={bm.masterDebug.raw} />
          )}
        </>)}

        {/* فیلدهای کمپین */}
        {dataStage?.meta?.fields?.length > 0 && (<>
          <div style={{ fontWeight: 800, color: '#34D399', margin: '10px 0 8px', fontSize: 12 }}>📋 فیلدهای ذخیره‌شده</div>
          {dataStage.meta.fields.map((f, i) => (
            <Row key={i} label={f.field_name} value={f.field_value} color="#4ADE80" />
          ))}
        </>)}

        {/* حراست */}
        {msg.clearance?.signatures?.length > 0 && (<>
          <div style={{ fontWeight: 800, color: msg.clearance.released ? '#34D399' : '#F87171', margin: '10px 0 8px', fontSize: 12 }}>
            🛡️ حراست — {msg.clearance.released ? 'آزاد' : 'متوقف'}
          </div>
          {msg.clearance.signatures.map((s, i) => (
            <div key={i} style={{ display: 'flex', gap: 6, padding: '4px 0', borderBottom: '1px solid var(--border)', alignItems: 'flex-start' }}>
              <span style={{ fontSize: 11, minWidth: 110, color: 'var(--text-3)' }}>
                {s.verdict === 'pass' ? '✅' : s.verdict === 'reject' ? '⛔' : '✋'} {s.title}
              </span>
              <span style={{ fontSize: 11.5, color: s.verdict === 'pass' ? '#34D399' : s.verdict === 'reject' ? '#F87171' : '#FBBF24' }}>{s.reason}</span>
            </div>
          ))}
          {msg.clearance?.masterDebug?.prompt && (
            <MasterDebugBlock prompt={msg.clearance.masterDebug.prompt} raw={msg.clearance.masterDebug.raw} label="استاد (از مسیر حراست)" />
          )}
        </>)}
      </div>
    );
  };

  // ناظر اصلی — کل مکالمه را AI بررسی و تصحیح می‌کند
  const [reviewing, setReviewing] = useState(false);
  const [reviewResult, setReviewResult] = useState(null);
  const runMainReview = async () => {
    setReviewing(true); setReviewResult(null);
    if (!apiKey) {
      setReviewing(false);
      toast('کلید Gemini را در تنظیمات وارد کنید', 'error');
      return;
    }
    // کل مکالمه + index اصلی هر پیام در چت (برای نشان‌گذاری در همان حباب)
    const targets = [];
    chatHistory.forEach((m, gi) => { if (m.role === 'assistant' && m.userText) targets.push({ ...m, _gi: gi }); });
    if (!targets.length) { setReviewing(false); toast('مکالمه‌ای برای بررسی نیست', 'info'); return; }

    try {
      // برچسبِ خوانا برای «ربات از چه مسیری جواب داد» — تا ناظر «رفتار» را بسنجد نه فقط برچسب
      const srcLabel = (s) => ({
        ml: 'موتور محلی جواب داد',
        ai_fallback: 'AI جواب داد',
        ai_browser: 'Gemini مرورگر جواب داد',
        script_faq: 'جواب آماده‌ی اسکریپت',
        moderation: 'فیلتر محتوا (پاسخ مرزی)',
        off_topic_ml: 'ربات این را «بی‌ربط» فرض کرد (یادگرفته)',
        no_ai: 'ربات نتوانست جواب دهد و «بی‌ربط» فرض کرد',
      }[s] || (s || 'نامشخص'));
      const stateOf = (m) => {
        const st = (m.trace || []).find(x => x.stage === 'state');
        return st ? String(st.title || '').replace(/^استیت فعلی:\s*/, '') : 'بدون اسکریپت فعال';
      };

      const intentList = intents.join('، ');
      // کور: حدسِ intentِ ربات را عمداً نشان نمی‌دهیم تا ناظر لنگر نیندازد
      const convText = targets.map((m, i) =>
        `${i + 1}. کاربر گفت: «${m.userText}»\n   مرحله‌ی اسکریپت: ${stateOf(m)}\n   رفتار ربات: ${srcLabel(m.source)} — جواب: «${(m.text || '').slice(0, 90)}»`
      ).join('\n');

      const prompt = `تو ناظر کیفیت ربات فروش فارسی آکادمی برنامه‌نویسی «متامارکت» هستی.
زیر یک مکالمه‌ی تست است. برای هر نوبت، «پیام کاربر»، «مرحله‌ی اسکریپت» و «رفتار/جواب ربات» آمده — ولی intentی که ربات حدس زده را عمداً نمی‌گویم تا مستقل قضاوت کنی.

برای هر نوبت این چهار چیز را تعیین کن:
۱) category — دقیقاً یکی از:
   - typo: غلط املاییِ یک پیام مرتبط (مثل «شلام»=سلام، «سلم»=سلام). باید مثل پیام درست رفتار شود، نه «بی‌ربط».
   - off_topic: کاملاً بی‌ربط به برنامه‌نویسی/دوره‌ها (آب‌وهوا، خنده، چرت‌وپرت).
   - abusive: توهین یا بی‌ادبی.
   - side: سوال حاشیه‌ایِ مرتبط (قیمت، آنلاین بودن، مدت، مدرک، اقساط...) که باید کوتاه جواب داده شود و بعد به سوالِ همان مرحله برگردد.
   - on_script: پاسخ واقعی کاربر به سوالِ همان مرحله که باید مکالمه را جلو ببرد.
   - greeting: احوالپرسی.
۲) ok — آیا رفتار/جوابِ ربات برای آن category مناسب بود؟ true یا false.
۳) correct_intent — فقط برای on_script/side/greeting، نام intent درست از لیست؛ برای بقیه null.
۴) reason — یک جمله‌ی خیلی کوتاه.

intentهای مجاز: ${intentList}

مکالمه:
${convText}

فقط و فقط یک آرایه‌ی JSON برگردان (بدون توضیح، بدون \`\`\`):
[{"i":1,"category":"typo","ok":false,"correct_intent":null,"reason":"..."}]`;

      const raw = await geminiGenerate(prompt, { temperature: 0, maxOutputTokens: 1200 });
      let parsed = [];
      try { parsed = JSON.parse(raw.replace(/```json|```/g, '').trim()); }
      catch { const mm = raw.match(/\[[\s\S]*\]/); if (mm) try { parsed = JSON.parse(mm[0]); } catch {} }

      const reviews = [];
      let learned = 0, flags = 0;
      const corrections = {}; // _gi → correct
      // فقط دسته‌هایی که آموزشِ آماری کمکشان می‌کند → quick-learn.
      // typo/off_topic/abusive با ok=false → «پرچمِ اصلاح قانون»، نه آلوده‌کردن مدل با مثالِ بی‌اثر.
      const LEARNABLE = ['on_script', 'side', 'greeting'];
      for (const it of parsed) {
        const t = targets[(it.i || 0) - 1];
        if (!t) continue;
        const category = String(it.category || '').trim().replace(/[^a-z_]/g, '');
        const ok = it.ok === true;
        const correct = String(it.correct_intent || '').trim().replace(/[^a-z_]/g, '');
        const botIntent = t.intent || '?';
        const reason = String(it.reason || '').slice(0, 120);

        if (correct && LEARNABLE.includes(category) && correct !== botIntent) {
          const lR = await window.api('/mlmetamarket/supervisor/quick-learn', { method: 'POST', body: { text: t.userText, intent: correct } });
          if (lR.success) { learned++; corrections[t._gi] = correct; reviews.push({ text: t.userText, category, ok, bot: botIntent, correct, reason, action: 'learned' }); }
          else reviews.push({ text: t.userText, category, ok, bot: botIntent, correct, reason, action: 'skip' });
        } else if (!ok) {
          flags++;
          reviews.push({ text: t.userText, category, ok, bot: botIntent, correct, reason, action: 'flag' });
        } else {
          reviews.push({ text: t.userText, category, ok, bot: botIntent, correct, reason, action: 'ok' });
        }
      }

      // نشان‌گذاری تصحیح‌ها در همان حباب‌های چت
      if (Object.keys(corrections).length)
        setChatHistory(h => h.map((m, i) => corrections[i] ? { ...m, reviewed: corrections[i], learned_now: corrections[i] } : m));

      setReviewing(false);
      setReviewResult({ reviews, learned, flags, source: 'gemini_browser' });
      const parts = [];
      if (learned) parts.push(`${fa(learned)} مورد به ML یاد داده شد`);
      if (flags)   parts.push(`${fa(flags)} مورد نیاز به اصلاح قانون`);
      toast(parts.length ? `ناظر: ${parts.join(' · ')}` : 'ناظر: همه‌چیز درست بود ✅', 'success');
    } catch (e) {
      setReviewing(false);
      toast(`Gemini خطا داد: ${e.message}`, 'error');
    }
  };

  // نقشه مسیر سیستم برای یک پیام
  const TraceMap = ({ trace }) => {
    if (!trace || !trace.length) return null;
    const colorOf = (stage) => ({
      start: '#94a3b8', brain: '#C084FC', trigger: '#FBBF24', route: '#60A5FA',
      freecourse: '#34D399', silent: '#64748b', engine: '#818CF8',
      state: '#F472B6', data: '#34D399', reply: '#4ADE80',
    }[stage] || '#94a3b8');
    return (
      <div style={{ marginTop: 8, background: 'var(--bg)', border: '1px solid var(--border)', borderRadius: 8, padding: '8px 10px' }}>
        <div style={{ fontSize: 10, color: 'var(--text-3)', marginBottom: 6, fontWeight: 700 }}>🗺️ مسیر در سیستم</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
          {trace.map((s, i) => {
            const c = colorOf(s.stage);
            const on = !!s.active;  // مرحله‌ی فعلی روشن، بقیه خاموش
            return (
            <div key={i} style={{ display: 'flex', gap: 8, position: 'relative', paddingBottom: i < trace.length - 1 ? 8 : 0,
              opacity: on ? 1 : 0.35, transition: 'opacity .2s' }}>
              {/* خط عمودی اتصال */}
              {i < trace.length - 1 && (
                <div style={{ position: 'absolute', right: 9, top: 18, bottom: 0, width: 2, background: 'var(--border)' }} />
              )}
              <div style={{ width: 20, height: 20, borderRadius: 5,
                background: on ? c : c + '22',
                border: `1px solid ${on ? c : c + '55'}`,
                boxShadow: on ? `0 0 10px ${c}99` : 'none',
                display: 'grid', placeItems: 'center', fontSize: 11, flexShrink: 0, zIndex: 1 }}>
                {s.icon}
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 11, fontWeight: on ? 800 : 700, color: c }}>
                  {s.title}{on && <span style={{ fontSize: 9, marginRight: 5, padding: '1px 5px', borderRadius: 6, background: c + '33' }}>اینجاست</span>}
                </div>
                {s.detail && <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 1, wordBreak: 'break-word' }}>{s.detail}</div>}
              </div>
            </div>
          );})}
        </div>
      </div>
    );
  };

  // بلوکِ حراست — تاییده + امضای هیئتِ معلمین زیر هر پیام
  const Clearance = ({ c, heldReply }) => {
    if (!c || !c.signatures) return null;
    const released = c.released;
    const vColor = (v) => v === 'pass' ? '#34D399' : v === 'reject' ? '#F87171' : '#FBBF24';
    const vIcon  = (v) => v === 'pass' ? '✅' : v === 'reject' ? '⛔' : '✋';
    const accent = released ? '#34D399' : '#F87171';
    return (
      <div style={{ marginTop: 8, background: 'var(--bg)', border: `1px solid ${accent}44`, borderRadius: 8, padding: '8px 10px' }}>
        <div style={{ fontSize: 11, fontWeight: 800, color: accent, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
          🛡️ حراست — {released ? 'مجوزِ خروج صادر شد' : 'پیام متوقف شد'}
          {c.source === 'verified' && <span style={{ fontSize: 9, padding: '1px 5px', borderRadius: 6, background: 'rgba(52,211,153,.18)', color: '#34D399' }}>تاییده‌ی DB</span>}
          {c.source === 'board' && <span style={{ fontSize: 9, padding: '1px 5px', borderRadius: 6, background: 'rgba(99,102,241,.18)', color: '#A5B4FC' }}>امضای زنده‌ی هیئت</span>}
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
          {c.signatures.map((s, i) => (
            <div key={i} style={{ display: 'flex', gap: 6, fontSize: 10.5, alignItems: 'flex-start' }}>
              <span style={{ flexShrink: 0 }}>{vIcon(s.verdict)}</span>
              <span style={{ flexShrink: 0, fontWeight: 700, color: vColor(s.verdict) }}>{s.title}:</span>
              <span style={{ color: 'var(--text-3)', wordBreak: 'break-word' }}>{s.reason}</span>
            </div>
          ))}
        </div>
        {!released && (
          <div style={{ marginTop: 6, fontSize: 10, color: 'var(--text-3)', borderTop: '1px solid var(--border)', paddingTop: 5 }}>
            پیشنویسِ متوقف‌شده: «{(heldReply || '').slice(0, 80)}»
          </div>
        )}
      </div>
    );
  };

  // دکمه دستی «به AI بده» زیر هر پاسخ
  const ReviewBtn = ({ msg, idx }) => {
    if (!msg.userText) return null;
    if (msg.learned_now) {
      return (
        <div style={{ marginTop: 6, fontSize: 11, color: '#4ADE80', display: 'flex', alignItems: 'center', gap: 5 }}>
          <Icon name="check-circle" size={13} /> AI گفت «<b>{msg.learned_now}</b>» — ML یاد گرفت
        </div>
      );
    }
    if (msg.reviewed) {
      return (
        <div style={{ marginTop: 6, fontSize: 11, color: 'var(--text-3)', display: 'flex', alignItems: 'center', gap: 5 }}>
          AI گفت: <b style={{ color: 'var(--text-2)' }}>{msg.reviewed}</b>
        </div>
      );
    }
    return (
      <button onClick={() => reviewOne(msg.userText, idx)} disabled={msg.reviewing}
        style={{ marginTop: 6, fontSize: 10.5, padding: '3px 9px', borderRadius: 8, cursor: 'pointer',
          background: 'rgba(99,102,241,.1)', border: '1px solid rgba(99,102,241,.3)', color: '#A5B4FC',
          display: 'inline-flex', alignItems: 'center', gap: 4 }}>
        {msg.reviewing ? '⏳ بررسی…' : '🔍 به AI بده تا تصحیح کند'}
      </button>
    );
  };

  const SourceBadge = ({ msg }) => {
    const { source, intent, confidence } = msg;
    if (!source) return null;
    const isML   = source === 'ml';
    const isAI   = source === 'ai_fallback';
    const isBrow = source === 'ai_browser';
    const isMod  = source === 'moderation';
    const isNoAi = source === 'no_ai';
    const isFaq  = source === 'script_faq';
    const isOffML = source === 'off_topic_ml';
    return (
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginTop: 6 }}>
        {msg.reVerified && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(56,189,248,.15)', color: '#38BDF8', fontFamily: 'monospace' }}>🔄 re-verified</span>}
        {isML   && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(52,211,153,.15)', color: '#34D399', fontFamily: 'monospace' }}>🤖 ML · {fa(confidence)}٪</span>}
        {isOffML && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(248,113,113,.12)', color: '#F87171', fontFamily: 'monospace' }}>🚫 ML یاد گرفته: بی‌ربط</span>}
        {isFaq  && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(167,139,250,.15)', color: '#C4B5FD', fontFamily: 'monospace' }}>📜 جواب آماده‌ی اسکریپت</span>}
        {isAI   && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(99,102,241,.15)', color: '#A5B4FC', fontFamily: 'monospace' }}>✨ AI · {fa(confidence)}٪</span>}
        {isBrow && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(56,189,248,.15)', color: '#38BDF8', fontFamily: 'monospace' }}>🌐 Gemini (مرورگر)</span>}
        {isMod  && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(248,113,113,.15)', color: '#F87171', fontFamily: 'monospace' }}>🛑 فیلتر محتوا</span>}
        {isNoAi && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(251,191,36,.15)', color: '#FBBF24', fontFamily: 'monospace' }}>⚠️ AI در دسترس نیست</span>}
        {intent && !isMod && !isNoAi && <span style={{ fontSize: 10, padding: '2px 7px', borderRadius: 10, background: 'rgba(148,163,184,.1)', color: '#94a3b8', fontFamily: 'monospace' }}>{intent}</span>}
      </div>
    );
  };

  // ─── پرونده‌ی حراست ───────────────────────────────────────────
  const [mainTab, setMainTab]           = useState('learning');  // 'learning' | 'clearance'
  const [clRows, setClRows]             = useState([]);
  const [clStats, setClStats]           = useState({});
  const [clTotal, setClTotal]           = useState(0);
  const [clLoading, setClLoading]       = useState(false);
  const [clStatus, setClStatus]         = useState('all');
  const [clSearch, setClSearch]         = useState('');
  const [clDetail, setClDetail]         = useState(null);   // پرونده‌ی باز‌شده
  const [clDetailData, setClDetailData] = useState(null);
  const [clDetailLoading, setClDetailLoading] = useState(false);

  const loadClearanceLog = (statusOverride, searchOverride) => {
    const st = statusOverride !== undefined ? statusOverride : clStatus;
    const sq = searchOverride !== undefined ? searchOverride : clSearch;
    setClLoading(true);
    window.api(`/mlmetamarket/supervisor/clearance-log?status=${st}&limit=100&search=${encodeURIComponent(sq)}`)
      .then(r => { if (r.success) { setClRows(r.rows || []); setClStats(r.stats || {}); setClTotal(r.total || 0); } })
      .finally(() => setClLoading(false));
  };

  const openDetail = (row) => {
    setClDetail(row);
    setClDetailData(null);
    setClDetailLoading(true);
    window.api(`/mlmetamarket/supervisor/clearance-log/${row.id}`)
      .then(r => { if (r.success) setClDetailData(r); })
      .finally(() => setClDetailLoading(false));
  };

  // جنسِ مشکل از روی امضاها — برای نمایش در جدول
  const faultType = (sigs) => {
    if (!Array.isArray(sigs)) return null;
    const rejected = sigs.filter(s => s.verdict === 'reject');
    const abstained = sigs.filter(s => s.verdict === 'abstain');
    if (rejected.length) return { label: `رد: ${rejected.map(s => s.title).join(' + ')}`, color: '#F87171' };
    if (abstained.length) return { label: `امتناع: ${abstained.map(s => s.title).join(' + ')}`, color: '#FBBF24' };
    return { label: 'همه تأیید کردند', color: '#34D399' };
  };

  const TABS = [
    { id: 'pending',  label: 'در انتظار' },
    { id: 'approved', label: 'تأیید شده' },
    { id: 'rejected', label: 'رد شده' },
  ];

  return (
    <div style={{ padding: '20px 24px', maxWidth: 1100, margin: '0 auto' }}>

      {/* هدر */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 16 }}>
        <div style={{ width: 46, height: 46, borderRadius: 8, background: 'rgba(99,102,241,.12)', border: '1px solid rgba(99,102,241,.28)', display: 'grid', placeItems: 'center' }}>
          <Icon name="shield-check" size={22} color="#818CF8" />
        </div>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 18, fontWeight: 800, color: 'var(--text)' }}>ناظر یادگیری</div>
          <div style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 3 }}>
            هر خروجی باید تاییده‌ی امضاشده‌ی هیئتِ معلمین داشته باشد
          </div>
        </div>
        <Button kind="ghost" icon="settings-2" onClick={() => document.getElementById('sup-cfg').classList.toggle('hidden')}>تنظیمات</Button>
      </div>

      {/* دو تب اصلی */}
      <div style={{ display: 'flex', gap: 4, marginBottom: 16, borderBottom: '1px solid var(--border)', paddingBottom: 0 }}>
        {[
          { id: 'learning',  icon: 'brain',       label: 'یادگیری' },
          { id: 'clearance', icon: 'shield-check', label: 'پرونده‌ی حراست' },
        ].map(t => (
          <button key={t.id} onClick={() => { setMainTab(t.id); if (t.id === 'clearance') loadClearanceLog('all', ''); }}
            style={{ padding: '8px 18px', fontSize: 13, fontWeight: mainTab === t.id ? 700 : 500,
              borderBottom: mainTab === t.id ? '2px solid #818CF8' : '2px solid transparent',
              color: mainTab === t.id ? '#818CF8' : 'var(--text-3)',
              background: 'none', border: 'none', borderRadius: '6px 6px 0 0', cursor: 'pointer',
              display: 'flex', alignItems: 'center', gap: 6 }}>
            {t.label}
            {t.id === 'clearance' && (clStats.held > 0) && (
              <span style={{ fontSize: 10, padding: '1px 6px', borderRadius: 8, background: 'rgba(248,113,113,.2)', color: '#F87171' }}>
                {fa(clStats.held)} متوقف
              </span>
            )}
          </button>
        ))}
      </div>

      {mainTab === 'learning' && (<>

      {/* چت تست + نقشه مسیر */}
      <div className="card" style={{ marginBottom: 16, padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 8 }}>
          <Icon name="message-square" size={14} color="#94a3b8" />
          <span style={{ fontWeight: 700, fontSize: 13, color: 'var(--text-2)' }}>چت تست — مسیر سیستم</span>
          <Button kind="primary" icon="brain" style={{ marginRight: 'auto', padding: '4px 12px', fontSize: 11 }}
            loading={reviewing} onClick={runMainReview} disabled={!chatHistory.length}>
            🧠 ناظر اصلی — کل مکالمه را تصحیح کن
          </Button>
          <button onClick={() => {
            const lines = chatHistory.map(m => (m.role === 'user' ? 'کاربر' : 'ربات') + ': ' + m.text);
            navigator.clipboard.writeText(lines.join('\n'));
          }} disabled={!chatHistory.length}
            style={{ padding: '3px 10px', fontSize: 11, borderRadius: 8, cursor: chatHistory.length ? 'pointer' : 'default',
              background: 'rgba(99,102,241,.07)', border: '1px solid rgba(99,102,241,.25)', color: '#A5B4FC',
              display: 'inline-flex', alignItems: 'center', gap: 4, opacity: chatHistory.length ? 1 : 0.4 }}>
            📋 کپی چت
          </button>
          <Button kind="ghost" style={{ padding: '3px 8px', fontSize: 11 }}
            onClick={resetChat}>شروع تازه</Button>
        </div>

        {/* نتیجه ناظر اصلی */}
        {reviewResult && (
          <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', background: 'rgba(99,102,241,.05)' }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-2)', marginBottom: 8, display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
              <span>🧠 گزارش ناظر</span>
              <span className="badge" style={{ background: 'rgba(74,222,128,.15)', color: '#4ADE80' }}>{fa(reviewResult.learned || 0)} یادگیری</span>
              {!!reviewResult.flags && <span className="badge" style={{ background: 'rgba(251,191,36,.15)', color: '#FBBF24' }}>{fa(reviewResult.flags)} نیاز به اصلاح قانون</span>}
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              {(reviewResult.reviews || []).map((rv, i) => {
                const catColor = { typo: '#38BDF8', off_topic: '#F87171', abusive: '#FB7185', side: '#FBBF24', on_script: '#34D399', greeting: '#A5B4FC' }[rv.category] || '#94a3b8';
                return (
                <div key={i} title={rv.reason || ''} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11.5, padding: '4px 0', borderBottom: '1px solid var(--border)' }}>
                  <span style={{ flex: 1, color: 'var(--text-2)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{rv.text}</span>
                  <span style={{ fontFamily: 'monospace', fontSize: 10, padding: '1px 6px', borderRadius: 8, background: catColor + '22', color: catColor }}>{rv.category || '?'}</span>
                  {rv.action === 'learned' && <span style={{ fontFamily: 'monospace', fontSize: 10.5, color: '#4ADE80' }}>→ {rv.correct} ✓</span>}
                  {rv.action === 'flag' && <span style={{ fontSize: 10.5, color: '#FBBF24' }}>⚠ اصلاح قانون</span>}
                  {rv.action === 'ok' && <span style={{ fontSize: 10.5, color: '#34D399' }}>درست</span>}
                  {rv.action === 'skip' && <span style={{ fontSize: 10.5, color: '#94a3b8' }}>رد شد</span>}
                </div>
              );})}
            </div>
          </div>
        )}
        <div ref={chatScrollRef} style={{ height: 360, overflowY: 'auto', padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          {!chatHistory.length && (
            <div style={{ margin: 'auto', textAlign: 'center', color: 'var(--text-3)', fontSize: 12 }}>
              یک پیام بفرستید — مسیر کامل آن در سیستم را می‌بینید
            </div>
          )}
          {chatHistory.map((m, i) => (
            <div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: m.role === 'user' ? 'flex-end' : 'flex-start' }}>
              <div style={{
                maxWidth: '85%', padding: '8px 12px', borderRadius: 10, fontSize: 13,
                background: m.role === 'user' ? 'rgba(99,102,241,.18)' : 'var(--card)',
                color: 'var(--text)', border: '1px solid var(--border)',
              }}>{m.text}</div>
              {m.role === 'assistant' && (
                <div style={{ width: '85%' }}>
                  <SourceBadge msg={m} />
                  <Clearance c={m.clearance} heldReply={m.heldReply} />
                  <TraceMap trace={m.trace} />
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
                    <ReviewBtn msg={m} idx={i} />
                    <button onClick={() => toggleReport(i)}
                      style={{ fontSize: 10.5, padding: '3px 9px', borderRadius: 8, cursor: 'pointer',
                        background: openReports[i] ? 'rgba(99,102,241,.2)' : 'rgba(99,102,241,.07)',
                        border: '1px solid rgba(99,102,241,.3)', color: '#A5B4FC',
                        display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                      📊 {openReports[i] ? 'بستن گزارش' : 'گزارش کامل'}
                    </button>
                    {getMasterDebug(m) && (
                      <button onClick={() => toggleMasterPrompt(i)}
                        style={{ fontSize: 10.5, padding: '3px 9px', borderRadius: 8, cursor: 'pointer',
                          background: openMasterPrompts[i] ? 'rgba(56,189,248,.2)' : 'rgba(56,189,248,.07)',
                          border: '1px solid rgba(56,189,248,.3)', color: '#38BDF8',
                          display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                        📡 {openMasterPrompts[i] ? 'بستن پرامت' : 'پرامت استاد'}
                      </button>
                    )}
                  </div>
                  {openMasterPrompts[i] && (() => {
                    const md = getMasterDebug(m);
                    return md ? <MasterDebugBlock prompt={md.prompt} raw={md.raw} /> : null;
                  })()}
                  <FullReport msg={m} idx={i} />
                </div>
              )}
            </div>
          ))}
          {chatLoading && (
            <div style={{ alignSelf: 'flex-start', padding: '6px 12px', borderRadius: 10, background: 'var(--card)', border: '1px solid var(--border)', fontSize: 13, color: 'var(--text-3)' }}>
              در حال پردازش...
            </div>
          )}
          <div ref={chatEndRef} />
        </div>
        <div style={{ padding: '10px 14px', borderTop: '1px solid var(--border)', display: 'flex', gap: 8 }}>
          <input className="input" style={{ flex: 1 }} placeholder="پیام بفرستید..."
            value={chatInput} onChange={e => setChatInput(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && sendChat()} />
          <Button kind="primary" icon="send" onClick={sendChat} loading={chatLoading}>ارسال</Button>
        </div>
      </div>

      {/* تنظیمات (پنهان‌شونده) */}
      <div id="sup-cfg" className="hidden card" style={{ marginBottom: 16 }}>
        <div style={{ fontWeight: 700, fontSize: 13, color: 'var(--text-2)', marginBottom: 12 }}>تنظیمات Gemini</div>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
          <input className="input" type="password" placeholder="Gemini API Key" value={apiKey}
            onChange={e => setApiKey(e.target.value)} style={{ flex: 2, minWidth: 200, direction: 'ltr' }} />
          <input className="input" placeholder="gemini-2.0-flash" value={model}
            onChange={e => setModel(e.target.value)} style={{ flex: 1, minWidth: 150, direction: 'ltr' }} />
          <Button kind="primary" icon="save" onClick={saveConfig}>ذخیره</Button>
        </div>
      </div>

      {/* آمار */}
      <div style={{ display: 'flex', gap: 10, marginBottom: 16 }}>
        {TABS.map(t => (
          <div key={t.id} onClick={() => setStatus(t.id)} className="card" style={{
            flex: 1, padding: '10px 14px', cursor: 'pointer', textAlign: 'center',
            border: status === t.id ? '1px solid rgba(99,102,241,.5)' : undefined,
            background: status === t.id ? 'rgba(99,102,241,.08)' : undefined,
          }}>
            <div style={{ fontSize: 22, fontWeight: 800, color: t.id === 'pending' ? '#FBBF24' : t.id === 'approved' ? '#34D399' : '#F87171' }}>
              {fa(counts[t.id] || 0)}
            </div>
            <div className="text-xs text-3 mt-4">{t.label}</div>
          </div>
        ))}
      </div>

      {/* نوار ابزار */}
      <div style={{ display: 'flex', gap: 8, marginBottom: 14, alignItems: 'center' }}>
        <Button kind="ghost" icon="refresh-cw" onClick={loadQueue} loading={loading}>بروزرسانی</Button>
        {status === 'pending' && (
          <Button kind="primary" icon="bot" onClick={labelAll} loading={labeling}>
            {labeling ? 'در حال برچسب‌گذاری...' : 'پیشنهاد AI برای همه'}
          </Button>
        )}
        {status === 'approved' && (
          <Button kind="success" icon="zap" onClick={retrain} loading={retraining}>
            Retrain مدل
          </Button>
        )}
        <span className="text-xs text-3" style={{ marginRight: 'auto' }}>
          {fa(items.length)} مورد نمایش داده شده
        </span>
      </div>

      {/* جدول */}
      {!items.length && !loading ? (
        <Empty icon="inbox" title="موردی وجود ندارد"
          sub={status === 'pending' ? 'وقتی کاربران پیام بفرستند و مدل مطمئن نباشد اینجا نشان داده می‌شود' : ''} />
      ) : (
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <table className="table">
            <thead>
              <tr>
                <th style={{ width: '40%' }}>پیام کاربر</th>
                <th>تشخیص ML</th>
                <th>پیشنهاد AI</th>
                {status === 'approved' && <th>تأیید شده</th>}
                <th>زمان</th>
                {status === 'pending' && <th>عملیات</th>}
              </tr>
            </thead>
            <tbody>
              {items.map(item => (
                <tr key={item.id}>
                  <td style={{ maxWidth: 0 }}>
                    <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={item.text}>
                      {item.text}
                    </div>
                  </td>
                  <td>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                      <span className="badge">{item.ml_intent || '—'}</span>
                      <span style={{ fontSize: 11, color: confColor(item.ml_confidence), fontFamily: 'monospace' }}>
                        {fa(Math.round(item.ml_confidence))}٪
                      </span>
                    </div>
                  </td>
                  <td>
                    {item.ai_suggested_intent
                      ? <span className="badge ready">{item.ai_suggested_intent}</span>
                      : <span className="text-xs text-3">—</span>}
                  </td>
                  {status === 'approved' && (
                    <td><span className="badge ready">{item.approved_intent}</span></td>
                  )}
                  <td>
                    <span className="text-xs text-3" title={item.created_at}>
                      {item.created_at ? new Date(item.created_at).toLocaleDateString('fa-IR') : '—'}
                    </span>
                  </td>
                  {status === 'pending' && (
                    <td>
                      <div style={{ display: 'flex', gap: 6 }}>
                        <Button kind="success" style={{ padding: '4px 10px', fontSize: 11 }}
                          onClick={() => {
                            if (item.ai_suggested_intent) approve(item);
                            else { setEditItem(item); setEditIntent(item.ml_intent || ''); }
                          }}>
                          {item.ai_suggested_intent ? `✓ ${item.ai_suggested_intent}` : 'تأیید'}
                        </Button>
                        <Button kind="ghost" style={{ padding: '4px 8px', fontSize: 11 }}
                          onClick={() => { setEditItem(item); setEditIntent(item.ai_suggested_intent || item.ml_intent || ''); }}>
                          ✎
                        </Button>
                        <Button kind="danger" style={{ padding: '4px 8px', fontSize: 11 }}
                          onClick={() => reject(item.id)}>
                          ✕
                        </Button>
                      </div>
                    </td>
                  )}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      </>)}

      {/* ════════════════════════════════════════════════════════════
          تب حراست — بایگانی تمامِ خروج‌ها و توقف‌های ربات
          ════════════════════════════════════════════════════════════ */}
      {mainTab === 'clearance' && (
        <div>
          {/* نوار آمار */}
          <div style={{ display: 'flex', gap: 10, marginBottom: 14 }}>
            {[
              { key: 'all',      label: 'همه',     color: '#94a3b8' },
              { key: 'released', label: 'آزاد شده', color: '#34D399' },
              { key: 'held',     label: 'متوقف شده', color: '#F87171' },
            ].map(s => (
              <div key={s.key} onClick={() => { setClStatus(s.key); loadClearanceLog(s.key, clSearch); }}
                className="card" style={{ flex: 1, padding: '10px 14px', cursor: 'pointer', textAlign: 'center',
                  border: clStatus === s.key ? `1px solid ${s.color}66` : undefined,
                  background: clStatus === s.key ? `${s.color}11` : undefined }}>
                <div style={{ fontSize: 20, fontWeight: 800, color: s.color }}>
                  {fa(s.key === 'all' ? (clStats.released || 0) + (clStats.held || 0) : (clStats[s.key] || 0))}
                </div>
                <div className="text-xs text-3 mt-4">{s.label}</div>
              </div>
            ))}
          </div>

          {/* جستجو */}
          <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
            <input className="input" style={{ flex: 1 }} placeholder="جستجو در متنِ پیام یا پاسخ..."
              value={clSearch} onChange={e => setClSearch(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && loadClearanceLog(clStatus, clSearch)} />
            <Button kind="primary" icon="search" onClick={() => loadClearanceLog(clStatus, clSearch)} loading={clLoading}>جستجو</Button>
            <Button kind="ghost" icon="refresh-cw" onClick={() => loadClearanceLog(clStatus, '')} loading={clLoading}>همه</Button>
          </div>

          {/* جدول */}
          {!clRows.length && !clLoading ? (
            <div className="card" style={{ textAlign: 'center', padding: 32, color: 'var(--text-3)', fontSize: 13 }}>
              هنوز پرونده‌ای در بایگانی نیست — وقتی ربات پیامی بفرستد اینجا ثبت می‌شود
            </div>
          ) : (
            <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
              <table className="table">
                <thead>
                  <tr>
                    <th style={{ width: '30%' }}>پیامِ کاربر</th>
                    <th style={{ width: '30%' }}>پاسخِ ربات</th>
                    <th>وضعیت</th>
                    <th>جنسِ مشکل / تأیید</th>
                    <th>زمان</th>
                  </tr>
                </thead>
                <tbody>
                  {clRows.map(row => {
                    const ft = faultType(row.signatures);
                    const isHeld = row.status === 'held';
                    return (
                      <tr key={row.id} style={{ cursor: 'pointer' }} onClick={() => openDetail(row)}>
                        <td style={{ maxWidth: 0 }}>
                          <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 12 }} title={row.user_text}>
                            {row.user_text}
                          </div>
                        </td>
                        <td style={{ maxWidth: 0 }}>
                          <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 12, color: isHeld ? '#F87171' : 'var(--text-2)' }}
                            title={isHeld ? '(متوقف شد — به مشتری نرفت)' : row.reply_text}>
                            {isHeld ? '🛑 متوقف شد' : (row.reply_text || '—')}
                          </div>
                        </td>
                        <td>
                          <span style={{ fontSize: 11, padding: '2px 8px', borderRadius: 10, fontWeight: 700,
                            background: isHeld ? 'rgba(248,113,113,.15)' : 'rgba(52,211,153,.12)',
                            color: isHeld ? '#F87171' : '#34D399' }}>
                            {isHeld ? '🛑 متوقف' : '✅ آزاد'}
                          </span>
                        </td>
                        <td>
                          {ft && <span style={{ fontSize: 10.5, color: ft.color }}>{ft.label}</span>}
                        </td>
                        <td>
                          <span className="text-xs text-3" title={row.created_at}>
                            {row.created_at ? new Date(row.created_at).toLocaleString('fa-IR') : '—'}
                          </span>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}
        </div>
      )}

      {/* مودال جزئیاتِ پرونده */}
      {clDetail && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.7)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}
          onClick={e => { if (e.target === e.currentTarget) setClDetail(null); }}>
          <div className="card" style={{ width: '100%', maxWidth: 640, maxHeight: '85vh', overflowY: 'auto', padding: 24 }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
              <div style={{ fontWeight: 800, fontSize: 15 }}>🗂️ پرونده‌ی حراست #{clDetail.id}</div>
              <button onClick={() => setClDetail(null)} style={{ background: 'none', border: 'none', color: 'var(--text-3)', cursor: 'pointer', fontSize: 18 }}>✕</button>
            </div>

            {/* وضعیت کلی */}
            <div style={{ display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap' }}>
              <span style={{ fontSize: 12, padding: '3px 10px', borderRadius: 10, fontWeight: 700,
                background: clDetail.status === 'held' ? 'rgba(248,113,113,.15)' : 'rgba(52,211,153,.12)',
                color: clDetail.status === 'held' ? '#F87171' : '#34D399' }}>
                {clDetail.status === 'held' ? '🛑 متوقف — به مشتری نرفت' : '✅ آزاد — به مشتری رفت'}
              </span>
              <span style={{ fontSize: 11, padding: '3px 10px', borderRadius: 10, background: 'rgba(148,163,184,.1)', color: 'var(--text-3)' }}>
                {clDetail.created_at ? new Date(clDetail.created_at).toLocaleString('fa-IR') : '—'}
              </span>
              {clDetail.source && <span style={{ fontSize: 11, padding: '3px 10px', borderRadius: 10, background: 'rgba(99,102,241,.1)', color: '#A5B4FC' }}>
                منبع: {clDetail.source}
              </span>}
            </div>

            {/* پیام کاربر */}
            <div style={{ marginBottom: 12 }}>
              <div style={{ fontSize: 11, color: 'var(--text-3)', marginBottom: 4, fontWeight: 700 }}>پیامِ کاربر</div>
              <div style={{ background: 'rgba(99,102,241,.08)', borderRadius: 8, padding: '8px 12px', fontSize: 13 }}>{clDetail.user_text || '—'}</div>
            </div>

            {/* پاسخِ ربات */}
            <div style={{ marginBottom: 16 }}>
              <div style={{ fontSize: 11, color: 'var(--text-3)', marginBottom: 4, fontWeight: 700 }}>پاسخِ ربات</div>
              <div style={{ background: 'var(--bg)', borderRadius: 8, padding: '8px 12px', fontSize: 13, color: clDetail.status === 'held' ? '#F87171' : 'var(--text)' }}>
                {clDetail.status === 'held' ? '(متوقف شد — پاسخ به مشتری ارسال نشد)' : (clDetail.reply_text || '—')}
              </div>
            </div>

            {/* دلیلِ حراست */}
            {clDetail.reason && (
              <div style={{ marginBottom: 16, padding: '8px 12px', borderRadius: 8, background: 'rgba(251,191,36,.08)', border: '1px solid rgba(251,191,36,.2)' }}>
                <div style={{ fontSize: 11, color: '#FBBF24', fontWeight: 700, marginBottom: 4 }}>دلیلِ تصمیمِ حراست</div>
                <div style={{ fontSize: 12, color: 'var(--text-2)' }}>{clDetail.reason}</div>
              </div>
            )}

            {/* امضاهای هیئت */}
            <div style={{ marginBottom: 16 }}>
              <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-2)', marginBottom: 8 }}>امضاهای هیئتِ معلمین</div>
              {clDetailLoading ? (
                <div style={{ color: 'var(--text-3)', fontSize: 12 }}>در حال بارگذاری...</div>
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                  {((clDetailData?.row?.signatures || clDetail.signatures) || []).map((sig, i) => {
                    const vc = { pass: '#34D399', reject: '#F87171', abstain: '#FBBF24' }[sig.verdict] || '#94a3b8';
                    const vi = { pass: 'check-circle', reject: 'x-circle', abstain: 'alert-circle' }[sig.verdict] || 'circle';
                    return (
                      <div key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', padding: '8px 10px', borderRadius: 8, background: `${vc}0d`, border: `1px solid ${vc}33` }}>
                        <Icon name={vi} size={15} color={vc} style={{ flexShrink: 0, marginTop: 1 }} />
                        <div style={{ flex: 1 }}>
                          <div style={{ display: 'flex', gap: 6, alignItems: 'center', marginBottom: 3 }}>
                            <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>{sig.title || sig.teacher}</span>
                            <span style={{ fontSize: 10, padding: '1px 7px', borderRadius: 8, background: `${vc}22`, color: vc }}>
                              {sig.verdict === 'pass' ? 'تأیید' : sig.verdict === 'reject' ? 'رد' : 'امتناع'}
                            </span>
                          </div>
                          <div style={{ fontSize: 12, color: 'var(--text-2)' }}>{sig.reason || '—'}</div>
                          {sig.ts && <div style={{ fontSize: 10, color: 'var(--text-3)', marginTop: 2 }}>{new Date(sig.ts).toLocaleString('fa-IR')}</div>}
                        </div>
                      </div>
                    );
                  })}
                  {!((clDetailData?.row?.signatures || clDetail.signatures) || []).length && (
                    <div style={{ color: 'var(--text-3)', fontSize: 12 }}>امضایی ثبت نشده</div>
                  )}
                </div>
              )}
            </div>

            {/* سابقه‌ی همین پیام */}
            {clDetailData?.similar?.length > 0 && (
              <div>
                <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-2)', marginBottom: 8 }}>
                  سابقه‌ی همین پیام ({fa(clDetailData.similar.length)} بار قبلاً دیده شده)
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                  {clDetailData.similar.map(s => (
                    <div key={s.id} style={{ display: 'flex', gap: 10, alignItems: 'center', fontSize: 11, padding: '4px 8px', borderRadius: 6, background: 'var(--bg)' }}>
                      <span style={{ color: s.status === 'held' ? '#F87171' : '#34D399', fontWeight: 700 }}>
                        {s.status === 'held' ? '🛑' : '✅'}
                      </span>
                      <span style={{ color: 'var(--text-3)' }}>{new Date(s.created_at).toLocaleString('fa-IR')}</span>
                    </div>
                  ))}
                </div>
              </div>
            )}
          </div>
        </div>
      )}

      {/* دیالوگ ویرایش intent */}
      {editItem && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.6)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
          onClick={e => { if (e.target === e.currentTarget) setEditItem(null); }}>
          <div className="card" style={{ width: 420, padding: 20 }}>
            <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 12 }}>تأیید با intent دلخواه</div>
            <div style={{ background: 'var(--bg)', borderRadius: 6, padding: '8px 12px', marginBottom: 14, fontSize: 13, color: 'var(--text-2)' }}>
              {editItem.text}
            </div>
            <select className="input" value={editIntent} onChange={e => setEditIntent(e.target.value)} style={{ marginBottom: 14, direction: 'ltr' }}>
              <option value="">— انتخاب intent —</option>
              {intents.map(i => <option key={i} value={i}>{i}</option>)}
            </select>
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
              <Button kind="ghost" onClick={() => setEditItem(null)}>انصراف</Button>
              <Button kind="success" onClick={() => approve(editItem, editIntent)} disabled={!editIntent}>تأیید</Button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

window.Supervisor = Supervisor;
