/* ============================================================
   page-bots.jsx — Bot management: list, provision, register
   ============================================================ */

/* Re-bind parseDetail from primitives.jsx (defined there, exported via window) */
const parseDetail = window.parseDetail || ((body, status) => body?.message || `HTTP ${status}`);

/* ── Password once callout ── */
function PasswordOnceCallout({ password, onConfirmed }) {
  const [copied, setCopied]   = useState(false);
  const [confirmed, setConf]  = useState(false);

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(password);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (_e) {
      // Fallback for HTTP origins / Safari quirks.
      const ta = document.createElement('textarea');
      ta.value = password;
      ta.setAttribute('readonly', '');
      ta.style.position = 'absolute'; ta.style.left = '-9999px';
      document.body.appendChild(ta);
      ta.select();
      let ok = false;
      try { ok = document.execCommand && document.execCommand('copy'); } catch (_) { ok = false; }
      document.body.removeChild(ta);
      if (ok) { setCopied(true); setTimeout(() => setCopied(false), 2000); }
      else alert((window.appT||(s=>s))('Copy failed. Select the password manually and copy with Cmd/Ctrl+C.'));
    }
  };

  return (
    <div style={{
      background:'var(--bg-elev-2)', border:'2px solid var(--warn)',
      borderRadius:'var(--r-sm)', padding: 16, marginTop: 12,
    }}>
      <div style={{ display:'flex', alignItems:'center', gap: 6, marginBottom: 10 }}>
        <Icon name="alert" size={14} style={{ color:'var(--warn)' }}/>
        <span style={{ fontWeight: 600, fontSize: 13, color:'var(--warn)' }}>
          {(window.appT||(s=>s))('Copy this password now — it will never be shown again')}
        </span>
      </div>
      <div style={{ display:'flex', gap: 6, alignItems:'center', marginBottom: 12 }}>
        <code style={{
          flex: 1, padding: '8px 12px',
          background:'var(--bg-elev-3, var(--bg-elev-2))', borderRadius:'var(--r-xs)',
          fontFamily:'var(--font-mono)', fontSize: 13,
          border:'1px solid var(--line-hi)', overflowX:'auto', whiteSpace:'nowrap',
          userSelect:'all',
        }}>{password}</code>
        <button className="btn btn-ghost" onClick={copy} style={{ flexShrink: 0 }}>
          {copied ? <><Icon name="check" size={13}/> {(window.appT||(s=>s))('Copied')}</> : <><Icon name="copy" size={13}/> {(window.appT||(s=>s))('Copy')}</>}
        </button>
      </div>
      <label style={{ display:'flex', alignItems:'center', gap: 8, cursor:'pointer', fontSize: 13 }}>
        <input type="checkbox" checked={confirmed} onChange={e => setConf(e.target.checked)}
          style={{ width: 16, height: 16 }} />
        {(window.appT||(s=>s))('I have saved this password securely')}
      </label>
      {confirmed && (
        <button className="btn btn-pri" onClick={onConfirmed} style={{ marginTop: 12 }}>
          <Icon name="check" size={13}/> {(window.appT||(s=>s))('Done')}
        </button>
      )}
    </div>
  );
}

/* ── Edit Prompt Modal ── */
function EditPromptModal({ bot, onClose, onSaved }) {
  const [prompt, setPrompt] = useState(bot.system_prompt || '');
  const [name, setName]     = useState(bot.name || '');
  const [busy, setBusy]     = useState(false);
  const [err, setErr]       = useState('');
  const toast = useToast();

  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  const save = async () => {
    setBusy(true); setErr('');
    try {
      const r = await fetch(`/api/bots/${bot.id}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'same-origin',
        body: JSON.stringify({ name: name.trim() || bot.name, system_prompt: prompt }),
      });
      if (!r.ok) {
        const body = await r.json().catch(() => null);
        throw new Error(parseDetail(body, r.status));
      }
      toast.push({ msg: `${(window.appT||(s=>s))('Saved')} ${name.trim() || bot.name}`, kind: 'ok' });
      onSaved && onSaved();
      onClose();
    } catch(e) {
      setErr(e.message);
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="drawer-overlay" onClick={onClose} style={{ zIndex: 300 }}>
      <div onClick={e => e.stopPropagation()} style={{
        position:'fixed', top:'50%', left:'50%', transform:'translate(-50%,-50%)',
        background:'var(--bg-elev-1)', border:'1px solid var(--line-hi)',
        borderRadius:'var(--r-lg)', padding: 24, width: 560, maxWidth:'95vw',
        boxShadow:'0 24px 64px -16px #00000080', zIndex: 301, maxHeight:'90vh',
        display:'flex', flexDirection:'column',
      }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom: 8 }}>
          <div>
            <div style={{ fontWeight: 600, fontSize: 15 }}>{(window.appT||(s=>s))('Edit bot')}</div>
            <div className="muted mono" style={{ fontSize: 11 }}>{bot.matrix_id}</div>
          </div>
          <button className="btn btn-ghost btn-icon" onClick={onClose}><Icon name="x" size={14}/></button>
        </div>
        {err && <div className="badge badge-err" style={{ marginBottom: 12, padding:'6px 10px', fontSize: 12 }}><Icon name="alert" size={12}/> {err}</div>}
        <label className="login-label">{(window.appT||(s=>s))('Bot name')}</label>
        <input className="inp" value={name} onChange={e => setName(e.target.value)}
               placeholder={bot.name} style={{ width:'100%', marginBottom: 14 }} />
        <label className="login-label">{(window.appT||(s=>s))('System prompt')}</label>
        <textarea
          className="inp"
          value={prompt}
          onChange={e => setPrompt(e.target.value)}
          style={{ minHeight: 300, resize:'vertical', fontFamily:'var(--font-mono)', fontSize: 12, lineHeight: 1.6, flex: 1, marginBottom: 16 }}
          placeholder={(window.appT||(s=>s))('System prompt for this bot...')}
        />
        <div style={{ display:'flex', gap: 8, justifyContent:'flex-end' }}>
          <button className="btn btn-ghost" onClick={onClose}>{(window.appT||(s=>s))('Cancel')}</button>
          <button className="btn btn-pri" onClick={save} disabled={busy}>
            {busy ? (window.appT||(s=>s))('Saving...') : <><Icon name="check" size={13}/> {(window.appT||(s=>s))('Save prompt')}</>}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ── Provision New Bot Modal ── */
function ProvisionBotModal({ onClose, onCreated, personas, tones }) {
  const [localpart, setLocal]   = useState('');
  const [dispName, setDisp]     = useState('');
  const [persona, setPersona]   = useState('');
  const [tone, setTone]         = useState('neutral');
  const [prompt, setPrompt]     = useState('');
  const [rooms, setRooms]       = useState([]);
  const [allRooms, setAllRooms] = useState([]);
  const [busy, setBusy]         = useState(false);
  const [err, setErr]           = useState('');
  const [createdPwd, setCreatedPwd] = useState(null);
  const toast = useToast();

  const localpartValid = /^[a-z0-9._-]+$/.test(localpart);
  const preview = localpart ? `@${localpart}:k9.ms` : '@...:k9.ms';

  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape' && !createdPwd) onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose, createdPwd]);

  useEffect(() => {
    fetch('/api/rooms', { credentials:'same-origin' })
      .then(r => r.ok ? r.json() : [])
      .then(setAllRooms)
      .catch(() => {});
  }, []);

  // Auto-fill prompt when persona changes
  useEffect(() => {
    if (persona && personas[persona]?.system_prompt) {
      setPrompt(personas[persona].system_prompt);
    }
  }, [persona]);

  const toggleRoom = (id) => setRooms(v => v.includes(id) ? v.filter(x => x !== id) : [...v, id]);

  const submit = async () => {
    if (!localpart || !localpartValid) { setErr((window.appT||(s=>s))('localpart must be lowercase alphanumeric, dots, underscores, hyphens only')); return; }
    if (!dispName.trim()) { setErr((window.appT||(s=>s))('Display name is required')); return; }
    setBusy(true); setErr('');
    try {
      const r = await fetch('/api/bots/provision', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'same-origin',
        body: JSON.stringify({
          localpart: localpart.trim(),
          name: dispName.trim(),
          persona: persona || 'general',
          tone,
          system_prompt: prompt || undefined,
        }),
      });
      if (!r.ok) {
        const body = await r.json().catch(() => null);
        throw new Error(parseDetail(body, r.status));
      }
      const data = await r.json();
      if (data.password) {
        setCreatedPwd(data.password);
      } else {
        toast.push({ msg: window.appLang && window.appLang()==='ar' ? `تم تجهيز البوت ${dispName}` : `Bot ${dispName} provisioned`, kind: 'ok' });
        onCreated && onCreated();
        onClose();
      }
    } catch(e) {
      setErr(e.message);
    } finally {
      setBusy(false);
    }
  };

  const personaKeys = Object.keys(personas || {});
  const toneKeys    = Object.keys(tones || {});

  return (
    <div className="drawer-overlay" onClick={createdPwd ? null : onClose} style={{ zIndex: 300 }}>
      <div onClick={e => e.stopPropagation()} style={{
        position:'fixed', top:'50%', left:'50%', transform:'translate(-50%,-50%)',
        background:'var(--bg-elev-1)', border:'1px solid var(--line-hi)',
        borderRadius:'var(--r-lg)', padding: 24, width: 520, maxWidth:'95vw',
        boxShadow:'0 24px 64px -16px #00000080', zIndex: 301, maxHeight:'90vh', overflowY:'auto',
      }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom: 20 }}>
          <div style={{ fontWeight: 600, fontSize: 15 }}>{(window.appT||(s=>s))('Provision new bot')}</div>
          {!createdPwd && <button className="btn btn-ghost btn-icon" onClick={onClose}><Icon name="x" size={14}/></button>}
        </div>

        {createdPwd ? (
          <PasswordOnceCallout
            password={createdPwd}
            onConfirmed={() => { onCreated && onCreated(); onClose(); }}
          />
        ) : (
          <>
            {err && <div className="badge badge-err" style={{ marginBottom: 12, padding:'7px 12px' }}><Icon name="alert" size={13}/> {err}</div>}

            <div style={{ marginBottom: 14 }}>
              <label className="login-label">localpart *  ({(window.appT||(s=>s))('preview')}: <code style={{ fontSize:11 }}>{preview}</code>)</label>
              <input className="inp" value={localpart} onChange={e => setLocal(e.target.value.toLowerCase().replace(/[^a-z0-9._-]/g,''))}
                placeholder={(window.appT||(s=>s))('e.g. attendance.bot')} autoFocus
                style={{ borderColor: localpart && !localpartValid ? 'var(--err)' : undefined }} />
              {localpart && !localpartValid && <div style={{ fontSize:11, color:'var(--err)', marginTop:3 }}>{(window.appT||(s=>s))('Only lowercase a-z, 0-9, dots, underscores, hyphens')}</div>}
            </div>

            <div style={{ marginBottom: 14 }}>
              <label className="login-label">{(window.appT||(s=>s))('Display name')} *</label>
              <input className="inp" value={dispName} onChange={e => setDisp(e.target.value)} placeholder={(window.appT||(s=>s))('e.g. Attendance Coach')} />
            </div>

            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap: 12, marginBottom: 14 }}>
              <div>
                <label className="login-label">{(window.appT||(s=>s))('Persona')}</label>
                <select className="sel" value={persona} onChange={e => setPersona(e.target.value)} style={{ width:'100%' }}>
                  <option value="">{(window.appT||(s=>s))('Select persona...')}</option>
                  {personaKeys.map(k => <option key={k} value={k}>{personas[k].label || k}</option>)}
                </select>
              </div>
              <div>
                <label className="login-label">{(window.appT||(s=>s))('Tone')}</label>
                <select className="sel" value={tone} onChange={e => setTone(e.target.value)} style={{ width:'100%' }}>
                  {toneKeys.map(k => <option key={k} value={k}>{tones[k].label || k}</option>)}
                </select>
              </div>
            </div>

            <div style={{ marginBottom: 14 }}>
              <label className="login-label">{(window.appT||(s=>s))('System prompt (auto-filled from persona, editable)')}</label>
              <textarea className="inp" value={prompt} onChange={e => setPrompt(e.target.value)}
                style={{ minHeight: 140, resize:'vertical', fontFamily:'var(--font-mono)', fontSize: 12, lineHeight: 1.5 }}
                placeholder={(window.appT||(s=>s))('Custom system prompt...')} />
            </div>

            <div style={{ marginBottom: 20 }}>
              <label className="login-label">{(window.appT||(s=>s))('Assign rooms (optional)')}</label>
              <div style={{ border:'1px solid var(--line)', borderRadius:'var(--r-sm)', padding: 6, maxHeight: 120, overflowY:'auto', display:'flex', flexDirection:'column', gap: 3 }}>
                {allRooms.length === 0 && <div className="muted" style={{ fontSize: 12 }}>{(window.appT||(s=>s))('No rooms yet')}</div>}
                {allRooms.map(r => {
                  const checked = rooms.includes(r.id);
                  return (
                    <label key={r.id} style={{ display:'flex', alignItems:'center', gap: 6, cursor:'pointer', fontSize: 13, padding:'2px 4px', borderRadius:'var(--r-xs)', background: checked ? 'var(--bg-elev-2)' : 'transparent' }}>
                      <input type="checkbox" checked={checked} onChange={() => toggleRoom(r.id)} />
                      {r.display_name}
                      <span className="mono muted" style={{ fontSize: 10, marginLeft:'auto' }}>{r.room_type}</span>
                    </label>
                  );
                })}
              </div>
            </div>

            <div style={{ display:'flex', gap: 8, justifyContent:'flex-end' }}>
              <button className="btn btn-ghost" onClick={onClose}>{(window.appT||(s=>s))('Cancel')}</button>
              <button className="btn btn-pri" onClick={submit} disabled={busy || !localpart || !localpartValid}>
                {busy ? (window.appT||(s=>s))('Provisioning...') : <><Icon name="ai" size={13}/> {(window.appT||(s=>s))('Provision bot')}</>}
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

/* ── Register Existing Bot Modal ── */
function RegisterBotModal({ onClose, onCreated, personas, tones }) {
  const [matrixId, setMatrixId] = useState('');
  const [dispName, setDisp]     = useState('');
  const [token, setToken]       = useState('');
  const [persona, setPersona]   = useState('general');
  const [tone, setTone]         = useState('neutral');
  const [prompt, setPrompt]     = useState('');
  const [rooms, setRooms]       = useState([]);
  const [allRooms, setAllRooms] = useState([]);
  const [busy, setBusy]         = useState(false);
  const [err, setErr]           = useState('');
  const toast = useToast();

  const validMxId = /^@[^:]+:.+$/.test(matrixId.trim());

  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  useEffect(() => {
    fetch('/api/rooms', { credentials:'same-origin' })
      .then(r => r.ok ? r.json() : [])
      .then(setAllRooms)
      .catch(() => {});
  }, []);

  useEffect(() => {
    if (persona && personas[persona]?.system_prompt && !prompt) {
      setPrompt(personas[persona].system_prompt);
    }
  }, [persona]);

  const toggleRoom = (id) => setRooms(v => v.includes(id) ? v.filter(x => x !== id) : [...v, id]);

  const submit = async () => {
    if (!validMxId) { setErr((window.appT||(s=>s))('K9 Chat ID must match @user:server')); return; }
    if (!dispName.trim()) { setErr((window.appT||(s=>s))('Display name is required')); return; }
    if (!token.trim()) { setErr((window.appT||(s=>s))('Access token is required')); return; }
    setBusy(true); setErr('');
    try {
      const r = await fetch('/api/bots', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'same-origin',
        body: JSON.stringify({
          matrix_id: matrixId.trim(),
          name: dispName.trim(),
          access_token: token.trim(),
          persona,
          tone,
          system_prompt: prompt || undefined,
        }),
      });
      if (!r.ok) {
        const body = await r.json().catch(() => null);
        throw new Error(parseDetail(body, r.status));
      }
      toast.push({ msg: window.appLang && window.appLang()==='ar' ? `تم تسجيل البوت ${dispName}` : `Bot ${dispName} registered`, kind: 'ok' });
      onCreated && onCreated();
      onClose();
    } catch(e) {
      setErr(e.message);
    } finally {
      setBusy(false);
    }
  };

  const personaKeys = Object.keys(personas || {});
  const toneKeys    = Object.keys(tones || {});

  return (
    <div className="drawer-overlay" onClick={onClose} style={{ zIndex: 300 }}>
      <div onClick={e => e.stopPropagation()} style={{
        position:'fixed', top:'50%', left:'50%', transform:'translate(-50%,-50%)',
        background:'var(--bg-elev-1)', border:'1px solid var(--line-hi)',
        borderRadius:'var(--r-lg)', padding: 24, width: 500, maxWidth:'95vw',
        boxShadow:'0 24px 64px -16px #00000080', zIndex: 301, maxHeight:'90vh', overflowY:'auto',
      }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom: 20 }}>
          <div style={{ fontWeight: 600, fontSize: 15 }}>{(window.appT||(s=>s))('Register existing bot')}</div>
          <button className="btn btn-ghost btn-icon" onClick={onClose}><Icon name="x" size={14}/></button>
        </div>
        {err && <div className="badge badge-err" style={{ marginBottom: 12, padding:'7px 12px' }}><Icon name="alert" size={13}/> {err}</div>}

        <div style={{ marginBottom: 14 }}>
          <label className="login-label">{(window.appT||(s=>s))('K9 Chat ID')} *</label>
          <input className="inp" value={matrixId} onChange={e => setMatrixId(e.target.value)} placeholder="@bot.user:k9.ms" autoFocus
            style={{ borderColor: matrixId && !validMxId ? 'var(--err)' : undefined }} />
          {matrixId && !validMxId && <div style={{ fontSize:11, color:'var(--err)', marginTop:3 }}>{(window.appT||(s=>s))('Must match @user:server')}</div>}
        </div>
        <div style={{ marginBottom: 14 }}>
          <label className="login-label">{(window.appT||(s=>s))('Display name')} *</label>
          <input className="inp" value={dispName} onChange={e => setDisp(e.target.value)} placeholder={(window.appT||(s=>s))('Bot display name')} />
        </div>
        <div style={{ marginBottom: 14 }}>
          <label className="login-label">{(window.appT||(s=>s))('Access token')} *</label>
          <input className="inp" type="password" value={token} onChange={e => setToken(e.target.value)} placeholder="syt_..." />
        </div>

        <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap: 12, marginBottom: 14 }}>
          <div>
            <label className="login-label">{(window.appT||(s=>s))('Persona')}</label>
            <select className="sel" value={persona} onChange={e => setPersona(e.target.value)} style={{ width:'100%' }}>
              {personaKeys.map(k => <option key={k} value={k}>{personas[k].label || k}</option>)}
            </select>
          </div>
          <div>
            <label className="login-label">{(window.appT||(s=>s))('Tone')}</label>
            <select className="sel" value={tone} onChange={e => setTone(e.target.value)} style={{ width:'100%' }}>
              {toneKeys.map(k => <option key={k} value={k}>{tones[k].label || k}</option>)}
            </select>
          </div>
        </div>

        <div style={{ marginBottom: 14 }}>
          <label className="login-label">{(window.appT||(s=>s))('System prompt')}</label>
          <textarea className="inp" value={prompt} onChange={e => setPrompt(e.target.value)}
            style={{ minHeight: 100, resize:'vertical', fontFamily:'var(--font-mono)', fontSize: 12, lineHeight: 1.5 }}
            placeholder={(window.appT||(s=>s))('System prompt...')} />
        </div>

        <div style={{ marginBottom: 20 }}>
          <label className="login-label">{(window.appT||(s=>s))('Assign rooms')}</label>
          <div style={{ border:'1px solid var(--line)', borderRadius:'var(--r-sm)', padding: 6, maxHeight: 100, overflowY:'auto', display:'flex', flexDirection:'column', gap: 3 }}>
            {allRooms.length === 0 && <div className="muted" style={{ fontSize: 12 }}>{(window.appT||(s=>s))('No rooms yet')}</div>}
            {allRooms.map(r => {
              const checked = rooms.includes(r.id);
              return (
                <label key={r.id} style={{ display:'flex', alignItems:'center', gap: 6, cursor:'pointer', fontSize: 13, padding:'2px 4px', borderRadius:'var(--r-xs)', background: checked ? 'var(--bg-elev-2)' : 'transparent' }}>
                  <input type="checkbox" checked={checked} onChange={() => toggleRoom(r.id)} />
                  {r.display_name}
                </label>
              );
            })}
          </div>
        </div>

        <div style={{ display:'flex', gap: 8, justifyContent:'flex-end' }}>
          <button className="btn btn-ghost" onClick={onClose}>{(window.appT||(s=>s))('Cancel')}</button>
          <button className="btn btn-pri" onClick={submit} disabled={busy || !validMxId}>
            {busy ? (window.appT||(s=>s))('Registering...') : <><Icon name="check" size={13}/> {(window.appT||(s=>s))('Register')}</>}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ── Delete Bot Confirm Modal ── */
function DeleteBotModal({ bot, onClose, onDeleted }) {
  const [busy, setBusy] = useState(false);
  const [err, setErr]   = useState('');
  const toast = useToast();

  const confirm = async () => {
    setBusy(true); setErr('');
    try {
      const r = await fetch(`/api/bots/${bot.id}`, {
        method: 'DELETE', credentials: 'same-origin',
      });
      if (!r.ok) {
        const body = await r.json().catch(() => null);
        throw new Error(parseDetail(body, r.status));
      }
      toast.push({ msg: window.appLang && window.appLang()==='ar' ? `تم حذف ${bot.name} نهائيًا` : `${bot.name} permanently deleted`, kind: 'ok' });
      onClose();
      onDeleted();
    } catch(e) {
      setErr(e.message);
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" style={{ maxWidth: 420 }} onClick={e => e.stopPropagation()}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom: 16 }}>
          <span style={{ fontWeight: 600, fontSize: 15 }}>{(window.appT||(s=>s))('Delete bot')}</span>
          <button className="btn btn-ghost btn-icon" onClick={onClose}><Icon name="x" size={14}/></button>
        </div>
        <p style={{ fontSize: 13, marginBottom: 16 }}>
          {(window.appT||(s=>s))('Permanently delete')} <strong>{bot.name}</strong>{(window.appT||(s=>s))('? This cannot be undone. Any rooms assigned to this bot will lose their bot assignment.')}
        </p>
        {err && <div className="badge badge-err" style={{ marginBottom: 12, padding:'6px 10px', fontSize: 12 }}><Icon name="alert" size={12}/> {err}</div>}
        <div style={{ display:'flex', gap: 8, justifyContent:'flex-end' }}>
          <button className="btn btn-ghost" onClick={onClose} disabled={busy}>{(window.appT||(s=>s))('Cancel')}</button>
          <button
            className="btn btn-pri"
            style={{ background:'var(--err)', borderColor:'var(--err)' }}
            onClick={confirm}
            disabled={busy}
          >
            {busy ? (window.appT||(s=>s))('Deleting...') : (window.appT||(s=>s))('Delete permanently')}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ── Bot Card ── */
function BotCard({ bot, onRefresh, personas, tones }) {
  const [editPromptOpen, setEditPromptOpen] = useState(false);
  const [toggling, setToggling]             = useState(false);
  const [confirming, setConfirming]         = useState(false);
  const [deleteOpen, setDeleteOpen]         = useState(false);
  const toast = useToast();

  const toggleActive = async () => {
    setToggling(true);
    try {
      const r = await fetch(`/api/bots/${bot.id}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'same-origin',
        body: JSON.stringify({ active: !bot.active }),
      });
      if (!r.ok) {
        const body = await r.json().catch(() => null);
        throw new Error(parseDetail(body, r.status));
      }
      toast.push({ msg: `${bot.name} ${bot.active ? (window.appT||(s=>s))('deactivated') : (window.appT||(s=>s))('activated')}`, kind: 'ok' });
      onRefresh();
    } catch(e) {
      toast.push({ msg: `${(window.appT||(s=>s))('Toggle failed')}: ${e.message}`, kind: 'err' });
    } finally {
      setToggling(false);
    }
  };

  const deactivate = async () => {
    setConfirming(false);
    try {
      const r = await fetch(`/api/bots/${bot.id}/deactivate`, {
        method: 'POST', credentials: 'same-origin',
      });
      if (!r.ok) {
        const body = await r.json().catch(() => null);
        throw new Error(parseDetail(body, r.status));
      }
      toast.push({ msg: `${bot.name} ${(window.appT||(s=>s))('deactivated')}`, kind: 'ok' });
      onRefresh();
    } catch(e) {
      toast.push({ msg: `${(window.appT||(s=>s))('Deactivate failed')}: ${e.message}`, kind: 'err' });
    }
  };

  // Persona label
  const personaLabel = personas?.[bot.persona]?.label || bot.persona || '—';
  const toneLabel    = tones?.[bot.tone]?.label       || bot.tone    || '—';
  const hasPrompt    = bot.system_prompt && bot.system_prompt.trim().length > 0;

  // Initial badge color — hash persona name
  const initColor = (() => {
    const s = bot.persona || bot.name || '';
    let h = 0;
    for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) & 0xffff;
    return `hsl(${h % 360}, 55%, 45%)`;
  })();

  return (
    <div className="card" style={{ opacity: bot.active ? 1 : 0.55 }}>
      <div style={{ display:'flex', alignItems:'flex-start', gap: 12, marginBottom: 12 }}>
        {/* Avatar initial badge */}
        <div style={{
          width: 40, height: 40, borderRadius: 10,
          background: initColor, color: '#fff',
          display:'flex', alignItems:'center', justifyContent:'center',
          fontWeight: 700, fontSize: 15, flexShrink: 0,
        }}>
          {(bot.name || '?').charAt(0).toUpperCase()}
        </div>

        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display:'flex', alignItems:'center', gap: 8, flexWrap:'wrap' }}>
            <span style={{ fontSize: 14, fontWeight: 600 }}>{bot.name}</span>
            <Badge kind={bot.active ? 'ok' : 'mute'} dot>{bot.active ? (window.appT||(s=>s))('active') : (window.appT||(s=>s))('inactive')}</Badge>
          </div>
          <div className="mono muted" style={{ fontSize: 11, marginTop: 1 }}>{bot.matrix_id}</div>
        </div>

        {/* Active toggle */}
        <button
          className="btn btn-ghost"
          onClick={toggleActive}
          disabled={toggling}
          style={{ fontSize: 12, flexShrink: 0 }}
          title={bot.active ? (window.appT||(s=>s))('Deactivate bot') : (window.appT||(s=>s))('Activate bot')}
        >
          {toggling ? '...' : (bot.active ? (window.appT||(s=>s))('Disable') : (window.appT||(s=>s))('Enable'))}
        </button>
      </div>

      {/* Persona + Tone badges */}
      <div style={{ display:'flex', gap: 6, flexWrap:'wrap', marginBottom: 10 }}>
        <Badge kind="info">{personaLabel}</Badge>
        <Badge kind="mute">{toneLabel}</Badge>
        <Badge kind={hasPrompt ? 'ok' : 'warn'}>
          {hasPrompt ? <><Icon name="check" size={10}/> {(window.appT||(s=>s))('prompt set')}</> : <><Icon name="alert" size={10}/> {(window.appT||(s=>s))('default prompt')}</>}
        </Badge>
      </div>

      {/* Actions */}
      <div style={{ display:'flex', gap: 6, flexWrap:'wrap' }}>
        <button className="btn btn-ghost" onClick={() => setEditPromptOpen(true)} style={{ fontSize: 12 }}>
          <Icon name="edit" size={12}/> {(window.appT||(s=>s))('Edit prompt')}
        </button>
        {bot.active && (
          confirming ? (
            <div style={{ display:'flex', gap: 4 }}>
              <button className="btn btn-ghost" style={{ fontSize: 12, color:'var(--err)' }} onClick={deactivate}>{(window.appT||(s=>s))('Confirm deactivate')}</button>
              <button className="btn btn-ghost" style={{ fontSize: 12 }} onClick={() => setConfirming(false)}>{(window.appT||(s=>s))('Cancel')}</button>
            </div>
          ) : (
            <button className="btn btn-ghost" onClick={() => setConfirming(true)} style={{ fontSize: 12, color:'var(--err)' }}>
              <Icon name="x" size={12}/> {(window.appT||(s=>s))('Deactivate')}
            </button>
          )
        )}
        <button
          className="btn btn-ghost"
          onClick={() => setDeleteOpen(true)}
          style={{ fontSize: 12, color:'var(--err)', marginLeft:'auto' }}
          title={(window.appT||(s=>s))('Permanently delete this bot')}
        >
          <Icon name="trash" size={12}/> {(window.appT||(s=>s))('Delete')}
        </button>
      </div>

      {editPromptOpen && (
        <EditPromptModal
          bot={bot}
          onClose={() => setEditPromptOpen(false)}
          onSaved={onRefresh}
        />
      )}
      {deleteOpen && (
        <DeleteBotModal
          bot={bot}
          onClose={() => setDeleteOpen(false)}
          onDeleted={onRefresh}
        />
      )}
    </div>
  );
}

/* ── BotsPage ── */
function BotsPage() {
  const [bots, setBots]             = useState([]);
  const [personas, setPersonas]     = useState({});
  const [tones, setTones]           = useState({});
  const [loading, setLoading]       = useState(true);
  const [provisionOpen, setProvOpen] = useState(false);
  const [registerOpen, setRegOpen]   = useState(false);
  const toast = useToast();

  const load = async () => {
    setLoading(true);
    try {
      const [br, pr, tr] = await Promise.all([
        fetch('/api/bots',          { credentials:'same-origin' }),
        fetch('/api/bots/personas', { credentials:'same-origin' }),
        fetch('/api/bots/tones',    { credentials:'same-origin' }),
      ]);
      if (br.ok) setBots(await br.json());
      if (pr.ok) setPersonas(await pr.json());
      if (tr.ok) setTones(await tr.json());
    } catch(e) {
      toast.push({ msg: (window.appT||(s=>s))('Load failed') + ': ' + e.message, kind: 'err' });
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { load(); }, []);

  const activeCount   = bots.filter(b => b.active).length;
  const inactiveCount = bots.filter(b => !b.active).length;

  return (
    <div className="page">
      <PageHeader
        title={(window.appT||(s=>s))('Bots')}
        meta={`${activeCount} ${(window.appT||(s=>s))('active')} · ${inactiveCount} ${(window.appT||(s=>s))('inactive')} · ${bots.length} ${(window.appT||(s=>s))('total')}`}
        right={
          <div style={{ display:'flex', gap: 8 }}>
            <Refresh onClick={load} loading={loading} />
            <button className="btn btn-ghost" onClick={() => setRegOpen(true)}>
              <Icon name="plus" size={14}/> {(window.appT||(s=>s))('Register existing')}
            </button>
            <button className="btn btn-pri" onClick={() => setProvOpen(true)}>
              <Icon name="ai" size={14}/> {(window.appT||(s=>s))('Provision new bot')}
            </button>
          </div>
        }
      />

      {loading && bots.length === 0 ? (
        <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(300px, 1fr))', gap: 12 }}>
          <Skel h={140} /><Skel h={140} /><Skel h={140} />
        </div>
      ) : bots.length === 0 ? (
        <Empty icon={<Icon name="ai" size={24}/>} title={(window.appT||(s=>s))('No bots yet')} subtitle={(window.appT||(s=>s))('Provision or register a bot to get started.')} />
      ) : (
        <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(300px, 1fr))', gap: 12 }}>
          {bots.map(b => (
            <BotCard key={b.id} bot={b} onRefresh={load} personas={personas} tones={tones} />
          ))}
        </div>
      )}

      {provisionOpen && (
        <ProvisionBotModal
          onClose={() => setProvOpen(false)}
          onCreated={load}
          personas={personas}
          tones={tones}
        />
      )}
      {registerOpen && (
        <RegisterBotModal
          onClose={() => setRegOpen(false)}
          onCreated={load}
          personas={personas}
          tones={tones}
        />
      )}
    </div>
  );
}

window.BotsPage = BotsPage;
