// User-facing dashboard screens — wired to real API
const { useState: uSt, useEffect: uEf, useRef: uRf, useMemo: uMm } = React;

// ───── Helpers shared in user screens ─────
function useLoad(loader, deps = []) {
  const [data, setData] = uSt(null);
  const [loading, setLoading] = uSt(true);
  const [error, setError] = uSt(null);
  const reload = async () => {
    setLoading(true); setError(null);
    try { setData(await loader()); } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  };
  uEf(() => { reload(); }, deps);
  return { data, loading, error, reload, setData };
}

// ───────── Overview ─────────
function UserOverview({ goto }) {
  const toast = useToast();
  const me    = window.HDR_USER || {};
  const { data: stats } = useLoad(() => window.HDR_API.stats(), []);
  const { data: logs }  = useLoad(() => window.HDR_API.logs(20), []);

  const totals = stats?.totals || {};
  const byEp   = stats?.byEndpoint || [];

  const recent = (logs || []).slice(0, 6);

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">Namaste, {me.name?.split(' ')[0] || me.email?.split('@')[0]} <span style={{fontFamily:'var(--font-display)', fontWeight:400, color:'var(--text-muted)'}}>👋</span></h1>
          <p className="page-sub">{totals.reqs_30d || 0} requests in last 30 days. <span className="muted">Real-time stats below.</span></p>
        </div>
        <div className="actions">
          <button className="btn" onClick={() => goto('playground')}><I.play/> Open Playground</button>
          <button className="btn primary" onClick={() => goto('billing')}><I.plus/> Add credit</button>
        </div>
      </div>

      <div className="kpi-grid">
        <KPI label={<><I.wallet/> Wallet</>} value={U.rupees(me.wallet_paise || 0).replace(/\.\d+$/, '')} unit={'.' + ((me.wallet_paise||0)%100).toString().padStart(2,'0')}
             spark={[20,22,19,24,30,28,33]} sparkColor="var(--accent)"/>
        <KPI label={<><I.zap/> Requests (30d)</>} value={U.num(totals.reqs_30d || 0)}
             spark={[8,12,10,18,15,22,28]}/>
        <KPI label={<><I.spark/> Spent (30d)</>} value={U.rupees(totals.spent_30d || 0).split('.')[0]}
             spark={[12,16,14,22,19,26,28]} sparkColor="var(--info)"/>
        <KPI label={<><I.shield/> Tokens (30d)</>} value={U.num(totals.tokens_30d || 0)}
             spark={[28,24,26,20,22,18,20]} sparkColor="var(--pos)"/>
      </div>

      <div className="grid-32">
        <div className="card">
          <div className="card-head">
            <div>
              <div className="card-title">Activity by endpoint</div>
              <div className="card-sub">Last 30 days</div>
            </div>
          </div>
          {byEp.length === 0 ? (
            <div className="muted" style={{padding:'20px 0', textAlign:'center', fontSize: 13}}>
              No usage yet. <a href="#" onClick={e => { e.preventDefault(); goto('playground'); }} style={{color:'var(--accent)'}}>Open Playground →</a>
            </div>
          ) : (
            <div className="col" style={{gap: 14, marginTop: 8}}>
              {byEp.map(e => {
                const maxC = Math.max(...byEp.map(x => Number(x.c) || 0), 1);
                const pct = ((Number(e.c) / maxC) * 100).toFixed(0);
                return (
                  <div key={e.endpoint}>
                    <div className="row" style={{marginBottom: 6, fontSize: 13}}>
                      <span style={{textTransform:'capitalize', fontWeight: 500}}>{e.endpoint}</span>
                      <div className="spacer"/>
                      <span className="muted mono" style={{fontSize: 11.5}}>{e.c} reqs · {U.rupees(e.cost)}</span>
                    </div>
                    <div style={{height: 6, borderRadius: 3, background: 'var(--surface-2)', overflow:'hidden'}}>
                      <div style={{height: '100%', width: pct + '%', background: 'var(--accent)'}}/>
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </div>

        <div className="col" style={{gap:16}}>
          <div className="card" style={{background:'linear-gradient(135deg, var(--accent-soft), transparent), var(--surface)', borderColor:'var(--accent-line)'}}>
            <div className="card-head">
              <div className="card-title" style={{color:'var(--accent)'}}>Get started</div>
            </div>
            <div className="col" style={{gap:10}}>
              <div className="row gap-3"><div style={{width:22, height:22, borderRadius:'50%', background:'var(--pos-soft)', color:'var(--pos)', display:'grid', placeItems:'center', fontSize:11, fontWeight:600}}>✓</div><span>Account created</span></div>
              <div className="row gap-3"><div style={{width:22, height:22, borderRadius:'50%', background:'var(--pos-soft)', color:'var(--pos)', display:'grid', placeItems:'center', fontSize:11, fontWeight:600}}>✓</div><span>₹50 signup bonus claimed</span></div>
              <div className="row gap-3" style={{cursor:'pointer'}} onClick={() => goto('keys')}>
                <div style={{width:22, height:22, borderRadius:'50%', background:'var(--accent)', color:'var(--accent-fg)', display:'grid', placeItems:'center', fontSize:11, fontWeight:600}}>3</div>
                <span>Generate your first API key</span><I.arrowRight/>
              </div>
              <div className="row gap-3 muted" style={{cursor:'pointer'}} onClick={() => goto('agents')}>
                <div style={{width:22, height:22, borderRadius:'50%', background:'var(--surface-3)', color:'var(--text-muted)', display:'grid', placeItems:'center', fontSize:11, fontWeight:600}}>4</div>
                <span>Create your first agent</span>
              </div>
            </div>
          </div>

          <div className="card">
            <div className="card-head">
              <div className="card-title">Recent activity</div>
              <div className="spacer"/>
              <a href="#" onClick={e => { e.preventDefault(); goto('logs'); }} className="muted" style={{fontSize:12}}>View all →</a>
            </div>
            {recent.length === 0 ? (
              <div className="muted" style={{padding:'12px 0', fontSize: 13}}>No activity yet</div>
            ) : (
              <div className="col" style={{gap: 8}}>
                {recent.map(l => (
                  <div key={l.id} className="row" style={{fontSize: 12}}>
                    <span className="mono">{l.model_alias || 'smart'}</span>
                    <span className="muted" style={{textTransform:'capitalize'}}>· {l.endpoint}</span>
                    <div className="spacer"/>
                    <span className="muted mono" style={{fontSize:11}}>{U.rupees(l.cost_paise || 0)}</span>
                    <span className="muted" style={{fontSize:11, marginLeft: 10}}>{U.ago(new Date(l.created_at).getTime())}</span>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

// ───────── Playground ─────────
function UserPlayground() {
  const [model, setModel]     = uSt('smart');
  const [agent, setAgent]     = uSt('');
  const [system, setSystem]   = uSt('You are a helpful assistant.');
  const [input, setInput]     = uSt('Hello! Briefly introduce yourself.');
  const [temp, setTemp]       = uSt(0.7);
  const [maxTok, setMaxTok]   = uSt(1024);
  const [topP, setTopP]       = uSt(1.0);
  const [stopSeq, setStopSeq] = uSt('');
  const [format, setFormat]   = uSt('text');
  const [wantButtons, setWantButtons]   = uSt(false);
  const [wantDecision, setWantDecision] = uSt(false);
  const [busy, setBusy]       = uSt(false);
  const [msgs, setMsgs]       = uSt([]);
  const [showCode, setShowCode] = uSt(false);
  const { data: agents } = useLoad(() => window.HDR_API.listAgents(), []);
  const toast = useToast();

  const buildBody = (extraInput) => {
    const messages = [
      ...(system ? [{ role: 'system', content: system }] : []),
      ...msgs.map(m => ({ role: m.role, content: m.content })),
      ...(extraInput ? [{ role: 'user', content: extraInput }] : []),
    ];
    const body = { model, messages, temperature: temp, max_tokens: maxTok, top_p: topP };
    if (agent) body.agent = agent;
    if (stopSeq.trim()) body.stop = stopSeq.split(',').map(s => s.trim()).filter(Boolean);
    if (format === 'json') body.response_format = { type: 'json_object' };
    if (wantButtons) body.buttons = true;
    if (wantDecision) body.decision = true;
    return body;
  };

  const send = async (overrideInput) => {
    // Type-guard: button onClick passes the event, button-suggestion callbacks pass strings
    const inText = (typeof overrideInput === 'string' ? overrideInput : input).trim();
    if (!inText || busy) return;
    const userMsg = { role: 'user', content: inText };
    setMsgs(m => [...m, userMsg]);
    if (typeof overrideInput !== 'string') setInput('');
    setBusy(true);
    try {
      const r = await window.HDR_API.chat(buildBody(inText));
      if (r.error) throw new Error(r.error.message || r.error);
      const reply = r.choices[0].message.content;
      setMsgs(m => [...m, {
        role: 'assistant', content: reply,
        provider: model, cost: r.cost?.paise || 0,
        latency: r.latency_ms, tokens: r.usage?.total_tokens,
        buttons: r.buttons || null,
        decision: r.decision || null,
      }]);
    } catch (e) {
      toast('Failed: ' + (e.message || 'unknown'), 'neg');
    } finally { setBusy(false); }
  };

  const aliasInfo = C.aliases.find(a => a.slug === model);
  const aliasTag = aliasInfo?.tag || 'AI';

  // Live cURL preview
  const curlPreview = `curl https://api.hidrogen.in/v1/chat/completions \\
  -H "Authorization: Bearer hi_xxx" \\
  -H "Content-Type: application/json" \\
  -d '${JSON.stringify(buildBody(input), null, 2)}'`;

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">Playground</h1>
          <p className="page-sub">Test models, agents, and output formats. Costs deducted from your wallet at standard rates.</p>
        </div>
        <div className="actions">
          <button className="btn ghost sm" onClick={() => setShowCode(v => !v)}>
            <I.code/> {showCode ? 'Hide' : 'Show'} cURL
          </button>
          <button className="btn ghost sm" onClick={() => setMsgs([])}><I.refresh/> Reset</button>
        </div>
      </div>

      <div className="grid-23">
        {/* Chat panel */}
        <div className="card" style={{padding: 0, display:'flex', flexDirection:'column', height: 'calc(100vh - 230px)', minHeight: 480}}>
          {/* Top bar with model + agent + chain */}
          <div style={{padding: '12px 16px', borderBottom: '1px solid var(--border)'}}>
            <div className="row" style={{gap:10, flexWrap:'wrap', alignItems: 'center'}}>
              <div style={{display:'flex', alignItems:'center', gap: 6}}>
                <span className="muted" style={{fontSize: 11}}>Model</span>
                <select className="select" style={{minWidth: 130, padding: '5px 10px', fontSize: 12.5}} value={model} onChange={e => setModel(e.target.value)}>
                  {C.aliases.map(a => <option key={a.slug} value={a.slug}>{a.slug}</option>)}
                </select>
              </div>
              <div style={{display:'flex', alignItems:'center', gap: 6}}>
                <span className="muted" style={{fontSize: 11}}>Agent</span>
                <select className="select" style={{minWidth: 150, padding: '5px 10px', fontSize: 12.5}} value={agent} onChange={e => setAgent(e.target.value)}>
                  <option value="">— none —</option>
                  {(agents || []).map(a => <option key={a.id} value={a.slug}>{a.name}</option>)}
                </select>
              </div>
              <div className="spacer"/>
              <div className="row" style={{gap: 8, fontSize: 11}}>
                <span className="badge">{aliasTag}</span>
                {format === 'json' && <span className="badge accent">JSON mode</span>}
              </div>
            </div>
          </div>

          {/* Messages */}
          <div style={{flex: 1, padding: 18, overflowY: 'auto', display:'flex', flexDirection:'column', gap:14}}>
            {msgs.length === 0 && !busy && (
              <div style={{margin:'auto', textAlign:'center', maxWidth: 320}}>
                <div style={{display:'inline-grid', placeItems:'center', width: 48, height: 48, borderRadius: 12, background: 'var(--accent-soft)', color:'var(--accent)', marginBottom: 14}}>
                  <I.play size={20}/>
                </div>
                <div style={{fontWeight: 500, marginBottom: 4}}>Start a conversation</div>
                <div className="muted" style={{fontSize: 12.5, lineHeight: 1.5}}>
                  Pick a model and agent, type a message, hit ⏎ or Cmd+Enter.<br/>
                  Cost shown after each reply.
                </div>
              </div>
            )}
            {msgs.map((m, i) => (
              <div key={i} style={{maxWidth:'85%', alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start'}}>
                <div style={{
                  background: m.role === 'user' ? 'var(--accent-soft)' : 'var(--surface-2)',
                  border: '1px solid ' + (m.role === 'user' ? 'var(--accent-line)' : 'var(--border)'),
                  padding: '11px 14px', borderRadius: 12, fontSize: 13.5, lineHeight: 1.55,
                  whiteSpace: 'pre-wrap'
                }}>{m.content}</div>
                {m.role === 'assistant' && (
                  <>
                    <div className="row" style={{marginTop: 6, gap: 8, fontSize: 11, flexWrap:'wrap'}}>
                      <span className="badge accent">{m.provider}</span>
                      {m.latency && <span className="muted mono">{m.latency}ms</span>}
                      {m.tokens && <span className="muted mono">{m.tokens} tokens</span>}
                      {m.cost > 0 && <span className="muted mono">{U.rupees(m.cost)}</span>}
                      <button className="icon-btn" style={{width: 22, height: 22}} title="Copy" onClick={() => { navigator.clipboard.writeText(m.content); toast('Copied', 'pos'); }}><I.copy size={12}/></button>
                    </div>

                    {/* Suggested buttons */}
                    {Array.isArray(m.buttons) && m.buttons.length > 0 && (
                      <div className="row" style={{gap: 6, marginTop: 8, flexWrap:'wrap'}}>
                        {m.buttons.map((b, bi) => (
                          <button key={bi} className="btn ghost sm"
                                  style={{fontSize: 12, padding: '5px 12px'}}
                                  onClick={() => send(b)}>
                            {b}
                          </button>
                        ))}
                      </div>
                    )}

                    {/* Decision panel */}
                    {m.decision && (
                      <div style={{
                        marginTop: 8, padding: '8px 12px',
                        background: 'var(--accent-soft)',
                        border: '1px solid var(--accent-line)',
                        borderRadius: 8, fontSize: 12,
                      }}>
                        <div className="row" style={{gap: 8, marginBottom: 4}}>
                          <span className="badge accent" style={{textTransform: 'uppercase', fontSize: 9.5}}>
                            {m.decision.action || 'decision'}
                          </span>
                          {typeof m.decision.confidence === 'number' && (
                            <span className="muted mono" style={{fontSize: 10.5}}>
                              {Math.round(m.decision.confidence * 100)}%
                            </span>
                          )}
                        </div>
                        {m.decision.reason && (
                          <div className="muted" style={{fontSize: 11.5, marginBottom: 3}}>
                            <strong style={{color:'var(--text)'}}>Why:</strong> {m.decision.reason}
                          </div>
                        )}
                        {m.decision.next_step && (
                          <div className="muted" style={{fontSize: 11.5}}>
                            <strong style={{color:'var(--text)'}}>Next:</strong> {m.decision.next_step}
                          </div>
                        )}
                      </div>
                    )}
                  </>
                )}
              </div>
            ))}
            {busy && (
              <div style={{maxWidth:'85%'}}>
                <div style={{background:'var(--surface-2)', border:'1px solid var(--border)', padding:'11px 14px', borderRadius:12}}>
                  <span className="dim mono" style={{fontSize:13}}>● ● ●</span>
                </div>
              </div>
            )}
          </div>

          {/* Composer */}
          <div style={{padding: 14, borderTop: '1px solid var(--border)'}}>
            <div className="row" style={{gap: 10, alignItems:'flex-end'}}>
              <textarea className="textarea" rows={2} value={input} onChange={e => setInput(e.target.value)}
                        onKeyDown={e => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey || !e.shiftKey)) { if (!e.shiftKey) { e.preventDefault(); send(); } } }}
                        placeholder="Type a message. ⏎ to send, ⇧⏎ for newline."
                        style={{minHeight: 50, maxHeight: 120}}/>
              <button className="btn primary" onClick={() => send()} disabled={busy} style={{height: 50, padding: '0 20px'}}>
                {busy ? '…' : <><I.send/> Send</>}
              </button>
            </div>
          </div>
        </div>

        {/* Side panel */}
        <div className="col" style={{gap: 14}}>
          <div className="card">
            <div className="card-head"><div className="card-title">System prompt</div></div>
            <textarea className="textarea" rows={3} value={system} onChange={e => setSystem(e.target.value)}
                      placeholder="Define the assistant's role…"/>
            {agent && <div className="muted" style={{fontSize: 11, marginTop: 8, lineHeight: 1.4}}>
              <I.alert size={12} style={{verticalAlign: 'middle'}}/> Agent's system prompt will override this.
            </div>}
          </div>

          <div className="card">
            <div className="card-head"><div className="card-title">Output format</div></div>
            <div className="seg" style={{width: '100%'}}>
              <button className={format === 'text' ? 'active' : ''} onClick={() => setFormat('text')} style={{flex:1}}>Text</button>
              <button className={format === 'json' ? 'active' : ''} onClick={() => setFormat('json')} style={{flex:1}}>JSON</button>
            </div>
            {format === 'json' && (
              <div className="muted" style={{fontSize: 11, marginTop: 8, lineHeight: 1.4}}>
                Model will return valid JSON. Mention "JSON" in your system prompt for best results.
              </div>
            )}
          </div>

          <div className="card">
            <div className="card-head"><div className="card-title">Response add-ons</div></div>
            <div className="col" style={{gap: 10}}>
              <label className="row" style={{gap: 8, cursor: 'pointer', alignItems:'flex-start'}}>
                <input type="checkbox" checked={wantButtons} onChange={e => setWantButtons(e.target.checked)}
                       style={{marginTop: 2, accentColor:'var(--accent)'}}/>
                <div style={{flex:1}}>
                  <div style={{fontSize: 12.5, fontWeight: 500}}>Suggested buttons</div>
                  <div className="muted" style={{fontSize: 11, lineHeight: 1.45}}>
                    Returns 3 follow-up suggestions in <code className="mono">response.buttons</code>
                  </div>
                </div>
              </label>
              <label className="row" style={{gap: 8, cursor: 'pointer', alignItems:'flex-start'}}>
                <input type="checkbox" checked={wantDecision} onChange={e => setWantDecision(e.target.checked)}
                       style={{marginTop: 2, accentColor:'var(--accent)'}}/>
                <div style={{flex:1}}>
                  <div style={{fontSize: 12.5, fontWeight: 500}}>Decision hint</div>
                  <div className="muted" style={{fontSize: 11, lineHeight: 1.45}}>
                    Returns <code className="mono">{'{action, reason, next_step, confidence}'}</code> for flow logic
                  </div>
                </div>
              </label>
            </div>
          </div>

          <div className="card">
            <div className="card-head"><div className="card-title">Parameters</div></div>
            <div className="col" style={{gap: 14}}>
              <div className="field">
                <div className="label row"><span>Temperature</span><div className="spacer"/><span className="dim mono">{temp.toFixed(1)}</span></div>
                <input type="range" min="0" max="2" step="0.1" value={temp} onChange={e => setTemp(parseFloat(e.target.value))}/>
                <div className="dim" style={{fontSize: 10.5, marginTop: 2}}>0 = deterministic, 2 = creative</div>
              </div>
              <div className="field">
                <div className="label row"><span>Max tokens</span><div className="spacer"/><span className="dim mono">{maxTok}</span></div>
                <input type="range" min="64" max="4096" step="64" value={maxTok} onChange={e => setMaxTok(parseInt(e.target.value, 10))}/>
              </div>
              <div className="field">
                <div className="label row"><span>Top P</span><div className="spacer"/><span className="dim mono">{topP.toFixed(2)}</span></div>
                <input type="range" min="0" max="1" step="0.05" value={topP} onChange={e => setTopP(parseFloat(e.target.value))}/>
              </div>
              <div className="field">
                <div className="label">Stop sequences <span className="dim">(comma separated)</span></div>
                <input className="input mono" style={{fontSize: 12}} value={stopSeq} onChange={e => setStopSeq(e.target.value)} placeholder="\n\n, END"/>
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Bottom: Live cURL preview (toggleable) */}
      {showCode && (
        <div className="card" style={{padding: 0, marginTop: 16}}>
          <div style={{padding: '12px 18px', borderBottom: '1px solid var(--border)'}} className="row">
            <div className="card-title">Live cURL preview</div>
            <div className="card-sub" style={{marginLeft: 12}}>Updates as you change settings</div>
            <div className="spacer"/>
            <button className="btn ghost sm" onClick={() => { navigator.clipboard.writeText(curlPreview); toast('Copied', 'pos'); }}>
              <I.copy/> Copy
            </button>
          </div>
          <pre className="code-block" style={{maxHeight: 280, overflowY: 'auto'}}>{curlPreview}</pre>
        </div>
      )}
    </div>
  );
}

// ───────── Agents ─────────
function UserAgents() {
  const [open, setOpen] = uSt(false);
  const [editing, setEditing] = uSt(null);
  const toast = useToast();
  const { data: agents, reload } = useLoad(() => window.HDR_API.listAgents(), []);
  const { data: skillsLib, reload: reloadSkills } = useLoad(() => window.HDR_API.skillsLibrary(), []);
  const skillsList = skillsLib || [];

  const startCreate = () => {
    setEditing({ name: '', slug: '', default_model: 'smart', system_prompt: '', skill_ids: [], auto_select_skills: true });
    setOpen(true);
  };

  const startEdit = async (a) => {
    const full = await window.HDR_API.getAgent(a.id);
    setEditing({
      ...full,
      skill_ids: (full.skills || []).map(s => s.id),
    });
    setOpen(true);
  };

  const save = async () => {
    if (!editing.name || !editing.slug) { toast('Name and slug required', 'neg'); return; }
    try {
      if (editing.id) {
        await window.HDR_API.updateAgent(editing.id, editing);
        toast('Agent updated', 'pos');
      } else {
        const r = await window.HDR_API.createAgent(editing);
        if (r.error) throw new Error(r.error);
        toast('Agent created — open again to add training data', 'pos');
      }
      setOpen(false);
      reload();
    } catch (e) { toast('Failed: ' + (e.message || ''), 'neg'); }
  };

  const removeAgent = async () => {
    if (!confirm('Delete this agent?')) return;
    await window.HDR_API.deleteAgent(editing.id);
    toast('Deleted', 'pos');
    setOpen(false); reload();
  };

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">My Agents</h1>
          <p className="page-sub">Custom bots with skills attached. Call via <span className="mono">{`agent: "slug"`}</span> in your API request.</p>
        </div>
        <div className="actions">
          <button className="btn primary" onClick={startCreate}><I.plus/> New agent</button>
        </div>
      </div>

      <div className="grid-3" style={{gap: 14}}>
        {(agents || []).map(a => (
          <div key={a.id} className="card" style={{cursor:'pointer'}} onClick={() => startEdit(a)}>
            <div className="row" style={{marginBottom: 12}}>
              <div style={{width:36, height:36, borderRadius:8, background: pickColor(a.slug) + '33', color: pickColor(a.slug), display:'grid', placeItems:'center'}}>
                <I.bot/>
              </div>
              <div style={{flex:1}}>
                <div style={{fontWeight: 600}}>{a.name}</div>
                <div className="muted mono" style={{fontSize: 11}}>agent: "{a.slug}"</div>
              </div>
              <span className={'badge ' + (a.is_active ? 'pos' : 'neg')}>{a.is_active ? 'Active' : 'Off'}</span>
            </div>
            <div className="muted" style={{fontSize:12, lineHeight:1.4, marginBottom: 12, minHeight: 32}}>
              {a.description || 'No description'}
            </div>
            <div className="divider" style={{margin: '10px 0'}}/>
            <div className="row" style={{fontSize: 12}}>
              <span className="muted">Model</span>
              <span className="mono">{a.default_model}</span>
              <div className="spacer"/>
              <span className="muted">{U.num(a.total_calls || 0)} calls · {a.skill_count || 0} skills</span>
            </div>
          </div>
        ))}

        <button className="card" onClick={startCreate} style={{
          border: '1px dashed var(--border-strong)', background: 'transparent',
          display:'grid', placeItems:'center', minHeight: 180, color: 'var(--text-muted)',
          gap: 6, flexDirection: 'column', textAlign:'center'
        }}>
          <I.plus size={20}/>
          <div style={{fontWeight: 500}}>Create new agent</div>
          <div className="dim" style={{fontSize: 11.5}}>Custom AI bot with skills</div>
        </button>
      </div>

      <Modal open={open} onClose={() => setOpen(false)}
             title={editing?.id ? `Edit "${editing?.name}"` : 'Create agent'}
             sub="Skills auto-selected per query based on relevance"
             size="wide"
             foot={<>
               {editing?.id && <button className="btn ghost" onClick={removeAgent}><I.trash/> Delete</button>}
               <div className="spacer"/>
               <button className="btn ghost" onClick={() => setOpen(false)}>Cancel</button>
               <button className="btn primary" onClick={save}><I.check/> Save agent</button>
             </>}>
        {editing && <AgentForm value={editing} onChange={setEditing} skillsList={skillsList} reloadSkills={reloadSkills}/>}
      </Modal>
    </div>
  );
}

function AgentForm({ value, onChange, skillsList, reloadSkills }) {
  const update = (k, v) => onChange({ ...value, [k]: v });
  const toggleSkill = (sid) => {
    const cur = value.skill_ids || [];
    update('skill_ids', cur.includes(sid) ? cur.filter(x => x !== sid) : [...cur, sid]);
  };
  return (
    <div className="grid-23" style={{gap: 24}}>
      <div className="col" style={{gap: 14}}>
        <div className="row" style={{gap: 12}}>
          <div className="field" style={{flex:1}}>
            <div className="label">Agent name</div>
            <input className="input" value={value.name || ''} onChange={e => update('name', e.target.value)} placeholder="My CA Bot"/>
          </div>
          <div className="field" style={{flex:1}}>
            <div className="label">Slug <span className="dim">(URL-safe)</span></div>
            <input className="input mono" value={value.slug || ''} onChange={e => update('slug', e.target.value.toLowerCase().replace(/[^a-z0-9-]/g,'-'))} placeholder="my-ca-bot" disabled={!!value.id}/>
          </div>
        </div>
        <div className="field">
          <div className="label">Description</div>
          <input className="input" value={value.description || ''} onChange={e => update('description', e.target.value)} placeholder="What does this agent do?"/>
        </div>
        <div className="field">
          <div className="label">System prompt <span className="dim">(optional — skill prompts append)</span></div>
          <textarea className="textarea" rows={4} value={value.system_prompt || ''} onChange={e => update('system_prompt', e.target.value)} placeholder="You are a helpful assistant..."/>
        </div>
        <div className="row" style={{gap: 12}}>
          <div className="field" style={{flex:1}}>
            <div className="label">Default model</div>
            <select className="select" value={value.default_model || 'smart'} onChange={e => update('default_model', e.target.value)}>
              {C.aliases.filter(a => ['smart','smart-pro','fast','creative'].includes(a.slug)).map(a => <option key={a.slug} value={a.slug}>{a.slug}</option>)}
            </select>
          </div>
          <div className="field" style={{flex:1}}>
            <div className="label">Auto-select skills</div>
            <Switch on={!!value.auto_select_skills} onChange={v => update('auto_select_skills', v)}/>
          </div>
        </div>
      </div>

      <div>
        <div className="card-title" style={{marginBottom: 10}}>Available skills</div>
        <div className="col" style={{gap: 8, maxHeight: 360, overflow:'auto'}}>
          {(skillsList || []).map(s => {
            const checked = (value.skill_ids || []).includes(s.id);
            return (
              <div key={s.id} className="row" style={{
                padding: 10, borderRadius: 'var(--r-md)', cursor:'pointer',
                background: checked ? 'var(--accent-soft)' : 'var(--surface-2)',
                border: '1px solid ' + (checked ? 'var(--accent-line)' : 'var(--border)'),
              }} onClick={() => toggleSkill(s.id)}>
                <input type="checkbox" checked={checked} readOnly style={{accentColor:'var(--accent)'}}/>
                <div style={{flex:1, minWidth: 0}}>
                  <div className="row" style={{gap: 6}}>
                    <div style={{fontWeight: 500, fontSize: 13}}>{s.name}</div>
                    {s.is_mine && <span className="badge accent" style={{fontSize: 9.5, padding: '1px 5px'}}>mine</span>}
                  </div>
                  <div className="muted" style={{fontSize: 11.5, lineHeight: 1.35, marginTop: 2}}>{s.description}</div>
                </div>
              </div>
            );
          })}
          {(!skillsList || skillsList.length === 0) && (
            <div className="muted" style={{fontSize: 13, padding: 20, textAlign: 'center'}}>
              No skills available. <br/>
              <span style={{fontSize: 12}}>Create your own in "My Skills" or wait for admin to publish.</span>
            </div>
          )}
        </div>
      </div>

      {/* Training / Knowledge section — shown only for saved agents */}
      {value.id && (value.skill_ids || []).length > 0 && (
        <div style={{borderTop: '1px solid var(--border)', paddingTop: 18, marginTop: 6}}>
          <div className="card-title" style={{marginBottom: 4}}>
            <I.book/> Training data
          </div>
          <div className="card-sub" style={{marginBottom: 14}}>
            Train this agent's attached skills with PDFs, websites, APIs, or pasted text. Knowledge is auto-loaded into context when the skill is active.
          </div>
          <AgentTrainingSection
            agent={value}
            onChange={onChange}
            attachedSkillIds={value.skill_ids || []}
            skillsList={skillsList || []}
            reloadSkills={reloadSkills}
          />
        </div>
      )}
      {value.id && (value.skill_ids || []).length === 0 && (
        <div style={{borderTop: '1px solid var(--border)', paddingTop: 18, marginTop: 6}}>
          <div className="card-title" style={{marginBottom: 4}}>
            <I.book/> Training data
          </div>
          <div className="card-sub" style={{marginBottom: 14}}>
            Add PDFs, websites, APIs, or pasted text. The agent will use this as reference knowledge.
          </div>
          <AgentTrainingSection
            agent={value}
            onChange={onChange}
            attachedSkillIds={[]}
            skillsList={skillsList || []}
            reloadSkills={reloadSkills}
          />
        </div>
      )}
      {!value.id && (
        <div style={{
          fontSize: 12, padding: 12, marginTop: 4,
          background: 'var(--accent-soft)',
          border: '1px solid var(--accent-line)',
          borderRadius: 'var(--r-md)',
          lineHeight: 1.5,
        }}>
          <strong>📚 Want to add PDFs, websites, APIs as training data?</strong><br/>
          <span className="muted">
            Click <strong style={{color:'var(--text)'}}>Save agent</strong> below — the form will stay open and the training section will appear here.
          </span>
        </div>
      )}
    </div>
  );
}

// ───────── Training section: aggregates knowledge across agent's skills ─────────
function AgentTrainingSection({ agent, onChange, attachedSkillIds, skillsList, reloadSkills }) {
  const [selectedSkillId, setSelectedSkillId] = uSt(null);
  const [creating, setCreating] = uSt(false);
  const toast = useToast();

  // Only show skills that are user-owned (can't add knowledge to global admin skills)
  const editableSkills = (skillsList || []).filter(
    s => attachedSkillIds.includes(s.id) && s.is_mine
  );
  const readOnlySkills = (skillsList || []).filter(
    s => attachedSkillIds.includes(s.id) && !s.is_mine
  );

  // Default to first editable skill
  uEf(() => {
    if (!selectedSkillId && editableSkills.length > 0) {
      setSelectedSkillId(editableSkills[0].id);
    }
  }, [editableSkills.length]);

  // One-click: create a private skill for this agent + auto-attach
  const createCustomSkill = async () => {
    if (!agent?.id || !agent?.slug) {
      toast('Save the agent first', 'neg'); return;
    }
    setCreating(true);
    try {
      const baseSlug = `${agent.slug}-knowledge`.toLowerCase().replace(/[^a-z0-9-]/g, '-').slice(0, 60);
      const skill = await window.HDR_API.createUserSkill({
        slug: baseSlug,
        name: `${agent.name} — Knowledge`,
        description: `Training data attached to ${agent.name}`,
        system_prompt: '',
        category: 'agent-specific',
        is_active: true,
      });
      if (skill.error) throw new Error(skill.error);

      // Attach the new skill to the agent
      const newSkillIds = [...new Set([...attachedSkillIds, skill.id])];
      await window.HDR_API.updateAgent(agent.id, { ...agent, skill_ids: newSkillIds });

      // Refresh skills list + sync parent agent state
      if (reloadSkills) await reloadSkills();
      if (onChange) onChange({ ...agent, skill_ids: newSkillIds });
      setSelectedSkillId(skill.id);
      toast('Custom skill created — now upload PDFs / URLs below', 'pos');
    } catch (e) {
      const msg = e.message || '';
      if (msg.includes('exists')) toast('A skill with similar slug exists. Edit it from "My Skills".', 'neg');
      else toast('Failed: ' + msg, 'neg');
    } finally { setCreating(false); }
  };

  // No editable skills attached → show "Create custom skill" CTA
  if (editableSkills.length === 0) {
    return (
      <div style={{
        padding: 16, background: 'var(--surface-2)',
        border: '1px dashed var(--border)', borderRadius: 'var(--r-md)',
      }}>
        <div style={{fontSize: 13, fontWeight: 500, marginBottom: 6}}>
          {readOnlySkills.length > 0
            ? 'Built-in skills can\'t hold custom training data'
            : 'No training data attached yet'}
        </div>
        <div className="muted" style={{fontSize: 12, lineHeight: 1.55, marginBottom: 12}}>
          Create a private skill for this agent and we'll auto-attach it. Then you can upload PDFs, websites, APIs, or pasted text.
        </div>
        <button className="btn primary sm" onClick={createCustomSkill} disabled={creating}>
          <I.plus/> {creating ? 'Creating…' : `Create custom skill for "${agent?.name || 'this agent'}"`}
        </button>
        {readOnlySkills.length > 0 && (
          <div className="muted" style={{fontSize: 11, marginTop: 10, lineHeight: 1.5}}>
            <strong style={{color:'var(--text)'}}>Built-in skills attached:</strong>{' '}
            {readOnlySkills.map(s => s.name).join(', ')} — pre-trained by Hidrogen.
          </div>
        )}
      </div>
    );
  }

  return (
    <div className="col" style={{gap: 14}}>
      {/* Skill picker — only show if multiple editable skills */}
      {editableSkills.length > 1 && (
        <div className="field">
          <div className="label">Add training data to skill</div>
          <select className="select" value={selectedSkillId || ''}
                  onChange={e => setSelectedSkillId(parseInt(e.target.value, 10))}>
            {editableSkills.map(s => (
              <option key={s.id} value={s.id}>{s.name} ({s.slug})</option>
            ))}
          </select>
        </div>
      )}
      {editableSkills.length === 1 && (
        <div className="muted" style={{fontSize: 11.5}}>
          Adding training data to: <strong style={{color: 'var(--text)'}}>{editableSkills[0].name}</strong>
        </div>
      )}

      {/* Knowledge manager scoped to selected skill */}
      {selectedSkillId && <SkillKnowledgeManager skillId={selectedSkillId} key={selectedSkillId}/>}

      {/* Note about read-only skills */}
      {readOnlySkills.length > 0 && (
        <div className="muted" style={{fontSize: 11, marginTop: 4, lineHeight: 1.5, padding: 10, background: 'var(--surface-2)', borderRadius: 'var(--r-md)'}}>
          <strong style={{color: 'var(--text)'}}>Built-in skills attached:</strong>{' '}
          {readOnlySkills.map(s => s.name).join(', ')}. These come pre-trained by Hidrogen and can't be modified.
        </div>
      )}
    </div>
  );
}

// ───────── API Keys ─────────
function UserKeys() {
  const { data: keys, reload } = useLoad(() => window.HDR_API.listKeys(), []);
  const { data: agents } = useLoad(() => window.HDR_API.listAgents(), []);
  const [newKey, setNewKey] = uSt(null);
  const [newName, setNewName] = uSt('');
  const [showCreate, setShowCreate] = uSt(false);
  const [codeTab, setCodeTab] = uSt('curl');
  const [showAgent, setShowAgent] = uSt(false);
  const [showJSON, setShowJSON] = uSt(false);
  const toast = useToast();

  const create = async () => {
    if (!newName.trim()) { toast('Name required', 'neg'); return; }
    try {
      const r = await window.HDR_API.createKey(newName.trim());
      if (r.error) throw new Error(r.error);
      setNewKey(r.key || r.api_key);
      setNewName('');
      reload();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  const revoke = async (id) => {
    if (!confirm('Revoke this key? It will stop working immediately.')) return;
    await window.HDR_API.revokeKey(id).catch(() => {});
    await window.HDR_API.deleteKey(id).catch(() => {});
    toast('Key revoked', 'pos');
    reload();
  };

  // Sample agent slug to show in code
  const agentSlug = (agents && agents[0]) ? agents[0].slug : 'my-agent';
  const sampleKey = keys && keys[0] ? `${keys[0].key_prefix}_xxx` : 'hi_live_xxx';

  // Generate code examples — native methods, no SDK dependencies (Hidrogen looks standalone)
  const examples = {
    curl: `curl https://api.hidrogen.in/v1/chat/completions \\
  -H "Authorization: Bearer ${sampleKey}" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "smart",${showAgent ? `\n    "agent": "${agentSlug}",` : ''}
    "messages": [
      {"role": "user", "content": "Hello!"}
    ]${showJSON ? `,\n    "response_format": { "type": "json_object" }` : ''}
  }'`,

    node: `// Native fetch — no SDK required
const response = await fetch(
  "https://api.hidrogen.in/v1/chat/completions",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer ${sampleKey}",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "smart",${showAgent ? `\n      agent: "${agentSlug}",` : ''}
      messages: [{ role: "user", content: "Hello!" }],${showJSON ? `\n      response_format: { type: "json_object" },` : ''}
    }),
  }
);

const data = await response.json();
console.log(data.choices[0].message.content);`,

    python: `# Native requests — no SDK required
import requests

response = requests.post(
    "https://api.hidrogen.in/v1/chat/completions",
    headers={
        "Authorization": "Bearer ${sampleKey}",
        "Content-Type": "application/json",
    },
    json={
        "model": "smart",${showAgent ? `\n        "agent": "${agentSlug}",` : ''}
        "messages": [{"role": "user", "content": "Hello!"}]${showJSON ? `,\n        "response_format": {"type": "json_object"}` : ''}
    }
)

data = response.json()
print(data["choices"][0]["message"]["content"])`,

    php: `<?php
$ch = curl_init('https://api.hidrogen.in/v1/chat/completions');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ${sampleKey}',
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'model' => 'smart',${showAgent ? `\n    'agent' => '${agentSlug}',` : ''}
    'messages' => [
        ['role' => 'user', 'content' => 'Hello!']
    ]${showJSON ? `,\n    'response_format' => ['type' => 'json_object']` : ''}
]));
$response = json_decode(curl_exec($ch), true);
echo $response['choices'][0]['message']['content'];`,
  };

  const tabs = [
    { id: 'curl',   label: 'cURL' },
    { id: 'node',   label: 'Node.js' },
    { id: 'python', label: 'Python' },
    { id: 'php',    label: 'PHP' },
  ];

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">API Keys</h1>
          <p className="page-sub">Generate keys and integrate Hidrogen into your app. Standard REST API — works with any HTTP client.</p>
        </div>
        <div className="actions">
          <button className="btn primary" onClick={() => setShowCreate(true)}><I.plus/> New key</button>
        </div>
      </div>

      {/* Keys table */}
      <div className="card" style={{padding: 0, marginBottom: 24}}>
        <table className="table mobile-cards">
          <thead>
            <tr>
              <th>Name</th><th>Key prefix</th><th>Created</th><th>Last used</th><th style={{width: 60}}></th>
            </tr>
          </thead>
          <tbody>
            {(keys || []).map(k => (
              <tr key={k.id}>
                <td data-label="Name"><span style={{fontWeight: 500}}>{k.name}</span></td>
                <td data-label="Key"><span className="mono" style={{fontSize:12.5}}>{k.key_prefix}_…</span></td>
                <td data-label="Created" className="muted">{new Date(k.created_at).toLocaleDateString('en-IN', { day:'numeric', month:'short', year:'numeric' })}</td>
                <td data-label="Last used" className="muted">{k.last_used_at ? U.ago(new Date(k.last_used_at).getTime()) : 'Never'}</td>
                <td data-label="" style={{textAlign:'right'}}>
                  <button className="icon-btn" title="Revoke" onClick={() => revoke(k.id)}><I.trash/></button>
                </td>
              </tr>
            ))}
            {(keys || []).length === 0 && (
              <tr><td colSpan="5" style={{textAlign:'center', padding: 40}}>
                <div style={{display:'inline-flex', flexDirection: 'column', alignItems:'center', gap: 8}}>
                  <I.key size={24}/>
                  <div style={{fontWeight: 500, marginTop: 6}}>No keys yet</div>
                  <div className="muted" style={{fontSize: 12.5}}>Click "New key" to create your first API key.</div>
                </div>
              </td></tr>
            )}
          </tbody>
        </table>
      </div>

      {/* Big code examples section */}
      <div className="card" style={{padding: 0, marginBottom: 18}}>
        <div style={{padding: '16px 20px', borderBottom: '1px solid var(--border)'}}>
          <div className="row" style={{gap: 16, flexWrap: 'wrap'}}>
            <div style={{flex: 1, minWidth: 200}}>
              <div className="card-title">How to integrate</div>
              <div className="card-sub">Pick your language and try it now</div>
            </div>
            {/* Toggle buttons */}
            <div className="row" style={{gap: 8, flexWrap: 'wrap'}}>
              <button className={'btn sm ' + (showAgent ? 'primary' : 'ghost')}
                      onClick={() => setShowAgent(v => !v)}>
                <I.bot/> {showAgent ? '✓' : '+'} Use Agent
              </button>
              <button className={'btn sm ' + (showJSON ? 'primary' : 'ghost')}
                      onClick={() => setShowJSON(v => !v)}>
                <I.code/> {showJSON ? '✓' : '+'} JSON output
              </button>
            </div>
          </div>
        </div>

        {/* Language tabs */}
        <div className="row" style={{padding: '0 20px', gap: 0, borderBottom: '1px solid var(--border)'}}>
          {tabs.map(t => (
            <button key={t.id}
                    className={'code-tab' + (codeTab === t.id ? ' active' : '')}
                    onClick={() => setCodeTab(t.id)}>
              {t.label}
            </button>
          ))}
        </div>

        {/* Code block */}
        <div style={{position: 'relative'}}>
          <pre className="code-block">{examples[codeTab]}</pre>
          <button className="btn ghost sm" style={{position:'absolute', top: 12, right: 12}}
                  onClick={() => { navigator.clipboard.writeText(examples[codeTab]); toast('Copied', 'pos'); }}>
            <I.copy/> Copy
          </button>
        </div>

        {/* Info pills */}
        <div style={{padding: '12px 20px', borderTop: '1px solid var(--border)'}}>
          <div className="row" style={{gap: 20, flexWrap: 'wrap', fontSize: 12}}>
            <div className="row" style={{gap: 6}}>
              <span className="tag-dot" style={{ background: 'var(--accent)' }}/>
              <span className="muted">Endpoint:</span>
              <span className="mono">https://api.hidrogen.in/v1</span>
            </div>
            <div className="row" style={{gap: 6}}>
              <span className="muted">Auth:</span>
              <span className="mono">Authorization: Bearer hi_…</span>
            </div>
            <div className="row" style={{gap: 6}}>
              <span className="muted">Format:</span>
              <span className="mono">JSON / REST</span>
            </div>
          </div>
        </div>
      </div>

      {/* Side info cards */}
      <div className="grid-32" style={{gap: 16}}>
        <div className="card">
          <div className="card-head">
            <div>
              <div className="card-title">Output format options</div>
              <div className="card-sub">Control how the API responds</div>
            </div>
          </div>
          <table className="table" style={{margin: '-8px 0 0'}}>
            <thead><tr><th>Field</th><th>Values</th><th>Default</th></tr></thead>
            <tbody>
              <tr><td className="mono">response_format</td><td className="muted">text | json_object</td><td className="mono muted">text</td></tr>
              <tr><td className="mono">temperature</td><td className="muted">0.0 – 2.0</td><td className="mono muted">0.7</td></tr>
              <tr><td className="mono">max_tokens</td><td className="muted">1 – 8192</td><td className="mono muted">2048</td></tr>
              <tr><td className="mono">top_p</td><td className="muted">0.0 – 1.0</td><td className="mono muted">1.0</td></tr>
              <tr><td className="mono">stop</td><td className="muted">array of strings</td><td className="mono muted">null</td></tr>
              <tr><td className="mono">stream</td><td className="muted">true | false</td><td className="mono muted">false</td></tr>
              <tr><td className="mono">agent</td><td className="muted">your agent slug</td><td className="mono muted">—</td></tr>
            </tbody>
          </table>
        </div>

        <div className="card" style={{background:'linear-gradient(135deg, var(--accent-soft), transparent), var(--surface)', borderColor:'var(--accent-line)'}}>
          <div className="card-head">
            <div className="card-title" style={{color:'var(--accent)'}}>
              <I.shield style={{verticalAlign: 'middle', marginRight: 6}}/>Security
            </div>
          </div>
          <ul className="muted" style={{margin: 0, paddingLeft: 18, fontSize: 12.5, lineHeight: 1.8}}>
            <li>Never commit keys to git</li>
            <li>Use separate keys per environment</li>
            <li>Rotate keys every 90 days</li>
            <li>Revoke compromised keys immediately</li>
            <li>Use server-side, never client-side</li>
          </ul>
        </div>
      </div>

      <Modal open={showCreate} onClose={() => { setShowCreate(false); setNewKey(null); setNewName(''); }}
             title={newKey ? 'Key created' : 'Create API key'}
             sub={newKey ? 'Copy now — this key will not be shown again' : 'Give it a memorable name'}
             foot={newKey ? (
               <button className="btn primary" onClick={() => { setShowCreate(false); setNewKey(null); }}>Done</button>
             ) : (
               <>
                 <div className="spacer"/>
                 <button className="btn ghost" onClick={() => setShowCreate(false)}>Cancel</button>
                 <button className="btn primary" onClick={create}><I.check/> Create key</button>
               </>
             )}>
        {newKey ? (
          <div className="col" style={{gap: 14}}>
            <div style={{
              padding: 16, borderRadius: 'var(--r-lg)',
              background:'var(--accent-soft)', border:'1px solid var(--accent-line)',
              fontFamily:'var(--font-mono)', fontSize: 13, wordBreak:'break-all'
            }}>{newKey}</div>
            <button className="btn" onClick={() => { navigator.clipboard.writeText(newKey); toast('Copied', 'pos'); }}>
              <I.copy/> Copy key
            </button>
            <div className="muted" style={{fontSize: 12, lineHeight: 1.45}}>
              Store this securely. For your security we don't show full keys after creation.
            </div>
          </div>
        ) : (
          <div className="field">
            <div className="label">Key name</div>
            <input className="input" value={newName} onChange={e => setNewName(e.target.value)} placeholder="Production / Staging / Local dev" autoFocus/>
          </div>
        )}
      </Modal>
    </div>
  );
}

// ───────── Billing — AutoPay only ─────────
function UserBilling() {
  const me = window.HDR_USER || {};
  const { data: txs } = useLoad(() => window.HDR_API.transactions(), []);
  const { data: autopay, reload: reloadAuto, loading: apLoading } = useLoad(() => window.HDR_API.autoPayStatus(), []);
  const [amount, setAmount] = uSt(500);
  const [busy, setBusy] = uSt(false);
  const toast = useToast();

  const isActive = autopay?.enabled && (autopay?.status === 'active' || autopay?.status === 'authenticated');

  // Open Razorpay Subscriptions hosted page in NEW TAB
  const setupAutoPay = async () => {
    if (amount < 50) { toast('Minimum ₹50', 'neg'); return; }
    setBusy(true);
    try {
      const r = await window.HDR_API.autoPaySetup({ amount, interval: 'monthly' });
      if (r.error) throw new Error(r.error);

      if (!r.short_url) throw new Error('Razorpay did not return authorization URL');

      const w = window.open(r.short_url, '_blank');
      if (!w) {
        toast('Popup blocked. Redirecting…', 'pos');
        setTimeout(() => { window.location.href = r.short_url; }, 500);
        return;
      }
      toast('Razorpay opened in new tab. After authorization, come back here.', 'pos');
      setBusy(false);
    } catch (e) {
      toast(e.message || 'Failed to start setup', 'neg');
      setBusy(false);
    }
  };

  const cancelAutoPay = async () => {
    if (!confirm('Cancel auto-pay? Your wallet won\'t auto-recharge anymore.')) return;
    try {
      await window.HDR_API.autoPayCancel();
      toast('Auto-pay cancelled', 'pos');
      reloadAuto();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  const pauseAutoPay = async () => {
    try {
      await window.HDR_API.autoPayPause();
      toast('Auto-pay paused', 'pos');
      reloadAuto();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  const resumeAutoPay = async () => {
    try {
      await window.HDR_API.autoPayResume();
      toast('Auto-pay resumed', 'pos');
      reloadAuto();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  const reopenAuth = async () => {
    if (!autopay?.short_url) {
      await reloadAuto();
      toast('Refreshed — click Re-open again', 'pos');
      return;
    }
    const w = window.open(autopay.short_url, '_blank');
    if (!w) {
      toast('Popup blocked. Redirecting…', 'pos');
      setTimeout(() => { window.location.href = autopay.short_url; }, 500);
      return;
    }
    toast('Razorpay opened in new tab', 'pos');
  };

  // Manual "Sync now" button — for users to manually trigger payment reconciliation
  const syncNow = async () => {
    setBusy(true);
    try {
      const r = await window.HDR_API.autoPaySync();
      if (r.credited > 0) {
        toast(`Synced! ₹${(r.credited_paise/100).toLocaleString('en-IN')} credited (${r.credited} payment${r.credited > 1 ? 's' : ''})`, 'pos');
        setTimeout(() => location.reload(), 1500);
      } else {
        toast('Already up to date — no new payments found', 'pos');
        reloadAuto();
      }
    } catch (e) { toast('Sync failed: ' + e.message, 'neg'); }
    finally { setBusy(false); }
  };

  // Reset / start over — cancel pending subscription, clear state
  const resetAutoPay = async () => {
    if (!confirm('Cancel the pending setup and start fresh?')) return;
    try {
      await window.HDR_API.autoPayCancel();
      toast('Cleared. You can set up auto-pay again.', 'pos');
      reloadAuto();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">Billing</h1>
          <p className="page-sub">Never run out of credits. Auto-pay charges your card / UPI automatically when needed.</p>
        </div>
      </div>

      <div className="grid-23" style={{gap: 16}}>
        {/* Wallet balance card */}
        <div className="card" style={{padding: 28, background:'linear-gradient(135deg, var(--accent-soft), transparent), var(--surface)', borderColor:'var(--accent-line)'}}>
          <div className="muted" style={{fontSize: 13, marginBottom: 6}}>Wallet balance</div>
          <div style={{ fontFamily:'var(--font-display)', fontSize: 48, fontWeight: 300, letterSpacing: '-0.02em', lineHeight: 1 }}>
            {U.rupees(me.wallet_paise || 0).split('.')[0]}
            <span style={{ fontSize: 24, color:'var(--text-muted)' }}>.{((me.wallet_paise||0)%100).toString().padStart(2,'0')}</span>
          </div>
          <div className="muted" style={{fontSize: 12.5, marginTop: 8}}>Spent lifetime: {U.rupees(me.total_spent_paise || 0)}</div>

          {isActive && (
            <div style={{marginTop: 18, padding: '10px 12px', background: 'var(--pos-soft)', borderRadius: 'var(--r-md)', fontSize: 12}}>
              <div className="row" style={{gap: 6, marginBottom: 4}}>
                <span className="live-dot"/>
                <strong style={{color:'var(--pos)'}}>Auto-pay active</strong>
              </div>
              <div className="muted" style={{fontSize: 11.5}}>
                ₹{(autopay.amount_paise/100).toLocaleString('en-IN')} auto-charges per month
              </div>
            </div>
          )}
        </div>

        {/* AutoPay setup / management card */}
        <div className="card">
          {apLoading ? (
            <div className="muted" style={{padding: 14, fontSize: 13}}>Loading…</div>
          ) : isActive ? (
            /* Active state — clean, minimal */
            <>
              <div className="card-head">
                <div>
                  <div className="card-title row" style={{gap: 8}}>
                    <I.zap/> Auto-pay {autopay.status === 'paused' ? 'paused' : 'active'}
                  </div>
                  <div className="card-sub">
                    {autopay.status === 'paused'
                      ? 'Resume to continue auto-recharges.'
                      : 'Your wallet auto-recharges every month.'}
                  </div>
                </div>
                <div className="spacer"/>
                <span className={'badge ' + (autopay.status === 'paused' ? 'warn' : 'pos')}>
                  {autopay.status === 'paused' ? 'Paused' : 'Active'}
                </span>
              </div>

              <div style={{padding: '14px 16px', background: 'var(--surface-2)', borderRadius: 'var(--r-md)', marginTop: 8}}>
                <div className="muted" style={{fontSize: 11, marginBottom: 4}}>Amount per month</div>
                <div className="mono" style={{fontSize: 22, fontWeight: 500, fontFamily: 'var(--font-display)'}}>
                  {U.rupees(autopay.amount_paise || 0)}
                </div>
              </div>

              <div className="row" style={{gap: 8, marginTop: 16}}>
                {autopay.status === 'paused' ? (
                  <button className="btn primary sm" onClick={resumeAutoPay} style={{flex: 1}}>
                    <I.play/> Resume auto-pay
                  </button>
                ) : (
                  <button className="btn ghost sm" onClick={pauseAutoPay} style={{flex: 1}}>
                    Pause auto-pay
                  </button>
                )}
                <button className="btn ghost sm" onClick={syncNow} title="Sync recent payments">
                  <I.refresh/> Sync
                </button>
              </div>

              <div className="dim" style={{fontSize: 11, marginTop: 12, lineHeight: 1.5}}>
                To fully cancel the mandate, do it from your bank app or UPI app where you authorized.
              </div>
            </>
          ) : autopay?.status === 'created' || autopay?.status === 'pending' ? (
            /* Pending authorization — give clear way to retry or reset */
            <>
              <div className="card-head">
                <div>
                  <div className="card-title">Authorization pending</div>
                  <div className="card-sub">You started auto-pay setup but didn't finish</div>
                </div>
                <div className="spacer"/>
                <span className="badge warn">Pending</span>
              </div>

              <div style={{padding: '12px 14px', background: 'var(--surface-2)', borderRadius: 'var(--r-md)', marginTop: 8, fontSize: 12.5, lineHeight: 1.55}}>
                <div className="row" style={{justifyContent:'space-between', marginBottom: 4}}>
                  <span className="muted">Amount</span>
                  <span className="mono" style={{fontWeight: 500}}>{U.rupees(autopay.amount_paise || 0)}/mo</span>
                </div>
                <div className="muted" style={{fontSize: 11.5, marginTop: 8}}>
                  Re-open Razorpay to complete authorization, or start over with a different amount.
                </div>
              </div>

              <div className="row" style={{gap: 8, marginTop: 14}}>
                <button className="btn primary" onClick={reopenAuth} disabled={busy} style={{flex: 1}}>
                  <I.zap/> {busy ? 'Opening…' : 'Re-open Razorpay'}
                </button>
                <button className="btn ghost sm" onClick={resetAutoPay} disabled={busy}>
                  Start over
                </button>
              </div>
            </>
          ) : (
            /* Initial setup — AutoPay only */
            <>
              <div className="card-head">
                <div>
                  <div className="card-title row" style={{gap: 8}}><I.zap/> Set up auto-pay</div>
                  <div className="card-sub">Monthly auto-recharge via UPI AutoPay / Cards</div>
                </div>
              </div>

              <div className="row" style={{gap: 8, marginBottom: 12, flexWrap: 'wrap'}}>
                {[100, 500, 1000, 2000, 5000].map(amt => (
                  <button key={amt} className={'btn ghost sm ' + (amount === amt ? 'active' : '')}
                          style={amount === amt ? {borderColor:'var(--accent)', color:'var(--accent)'} : {}}
                          onClick={() => setAmount(amt)}>₹{amt.toLocaleString('en-IN')}</button>
                ))}
              </div>
              <div className="field">
                <div className="label">Amount per month</div>
                <input type="number" min="50" max="100000" className="input"
                       value={amount} onChange={e => setAmount(parseInt(e.target.value, 10) || 0)}/>
              </div>

              <button className="btn primary" style={{width:'100%', marginTop: 14, height: 42}}
                      onClick={setupAutoPay} disabled={busy || amount < 50}>
                <I.zap/> {busy ? 'Opening Razorpay…' : `Set up auto-pay — ₹${amount.toLocaleString('en-IN')}/mo`}
              </button>

              <div className="muted" style={{fontSize: 11.5, marginTop: 12, lineHeight: 1.55}}>
                <strong style={{color:'var(--text)'}}>How it works:</strong> First charge happens immediately on authorization. Future charges happen automatically every month. Pause anytime.
              </div>

              {/* Test mode notice — Razorpay test mode UPI AutoPay limitation */}
              <div style={{
                marginTop: 14,
                padding: '10px 12px',
                fontSize: 11.5,
                lineHeight: 1.5,
                background: 'var(--info-soft, var(--accent-soft))',
                border: '1px dashed var(--accent-line)',
                borderRadius: 'var(--r-md)',
                color: 'var(--text-muted)',
              }}>
                <strong style={{color: 'var(--text)'}}>💡 Test mode tip:</strong> UPI AutoPay can't be fully tested in Razorpay test mode.
                On the next screen, choose <strong style={{color: 'var(--text)'}}>Card</strong> instead, and use:
                <div className="mono" style={{
                  marginTop: 6,
                  fontSize: 11,
                  padding: '6px 8px',
                  background: 'var(--surface)',
                  borderRadius: 6,
                  lineHeight: 1.7,
                }}>
                  Card: 4111 1111 1111 1111<br/>
                  Expiry: 12/30 · CVV: 123 · OTP: 123456
                </div>
              </div>
            </>
          )}
        </div>
      </div>

      {/* Recent transactions */}
      <div className="card" style={{marginTop: 20, padding: 0}}>
        <div className="card-head" style={{padding: '16px 18px', borderBottom:'1px solid var(--border)'}}>
          <div className="card-title">Recent transactions</div>
        </div>
        <table className="table mobile-cards">
          <thead><tr><th>Type</th><th>Amount</th><th>Note</th><th>Balance after</th><th>When</th></tr></thead>
          <tbody>
            {(txs || []).map(t => {
              const amt = t.amount_paise;
              const sign = t.type === 'debit' ? -1 : 1;
              const cls = t.type === 'debit' ? 'neg' : (t.type === 'refund' ? 'info' : 'pos');
              return (
                <tr key={t.id}>
                  <td data-label="Type"><span className={'badge ' + cls}>{t.type}</span></td>
                  <td data-label="Amount" className="mono" style={{color: sign > 0 ? 'var(--pos)' : 'inherit'}}>{sign > 0 ? '+' : '-'}{U.rupees(amt)}</td>
                  <td data-label="Note" className="muted">{t.description || '—'}</td>
                  <td data-label="Balance after" className="mono muted">{U.rupees(t.balance_after || 0)}</td>
                  <td data-label="When" className="muted">{U.ago(new Date(t.created_at).getTime())}</td>
                </tr>
              );
            })}
            {(txs || []).length === 0 && (
              <tr><td colSpan="5" className="muted" style={{textAlign:'center', padding: 20}}>No transactions yet</td></tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ───────── Logs (user-facing OR admin) ─────────
function UserLogs({ admin }) {
  const [filters, setFilters] = uSt({ q: '', endpoint: '', status: '', model_alias: '' });
  const [data, setData] = uSt(null);
  const [loading, setLoading] = uSt(true);
  const inputRef = uRf(null);

  const load = async () => {
    setLoading(true);
    try {
      if (admin) {
        const qs = Object.entries(filters).filter(([,v]) => v).map(([k,v]) => `${k}=${encodeURIComponent(v)}`).join('&');
        setData(await window.HDR_API.adminLogs(qs));
      } else {
        setData(await window.HDR_API.logs(200));
      }
    } finally { setLoading(false); }
  };

  uEf(() => { load(); }, [admin]);

  const debounced = uRf(null);
  const updateFilter = (k, v) => {
    setFilters(f => ({ ...f, [k]: v }));
    if (debounced.current) clearTimeout(debounced.current);
    debounced.current = setTimeout(load, 300);
  };

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">{admin ? 'All Logs' : 'Logs'}</h1>
          <p className="page-sub">{admin ? 'Platform-wide request log.' : 'Your last 200 API calls.'}</p>
        </div>
        <div className="actions">
          <button className="btn" onClick={load}><I.refresh/> Refresh</button>
        </div>
      </div>

      {admin && (
        <div className="card" style={{padding: 14, marginBottom: 14}}>
          <div className="row" style={{gap: 8, flexWrap: 'wrap'}}>
            <input ref={inputRef} className="input" placeholder="Search email/name…" value={filters.q} onChange={e => updateFilter('q', e.target.value)} style={{maxWidth: 220}}/>
            <select className="select" value={filters.endpoint} onChange={e => updateFilter('endpoint', e.target.value)} style={{maxWidth: 160}}>
              <option value="">All endpoints</option>
              <option value="chat">Chat</option>
              <option value="image">Image</option>
              <option value="tts">TTS</option>
              <option value="stt">STT</option>
            </select>
            <select className="select" value={filters.status} onChange={e => updateFilter('status', e.target.value)} style={{maxWidth: 140}}>
              <option value="">All status</option>
              <option value="success">Success</option>
              <option value="failed">Failed</option>
            </select>
            <select className="select" value={filters.model_alias} onChange={e => updateFilter('model_alias', e.target.value)} style={{maxWidth: 140}}>
              <option value="">All aliases</option>
              {C.aliases.map(a => <option key={a.slug} value={a.slug}>{a.slug}</option>)}
            </select>
          </div>
        </div>
      )}

      <div className="card" style={{padding: 0}}>
        <table className="table mobile-cards">
          <thead>
            <tr>
              <th>Time</th>
              {admin && <th>User</th>}
              <th>Endpoint</th>
              <th>Alias</th>
              {admin && <th>Provider</th>}
              <th style={{textAlign:'right'}}>Tokens</th>
              <th style={{textAlign:'right'}}>Cost</th>
              <th style={{textAlign:'right'}}>Latency</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            {loading && <tr><td colSpan="9" className="muted" style={{textAlign:'center', padding: 24}}>Loading…</td></tr>}
            {!loading && (data || []).length === 0 && (
              <tr><td colSpan="9" className="muted" style={{textAlign:'center', padding: 24}}>No requests match.</td></tr>
            )}
            {!loading && (data || []).map(l => (
              <tr key={l.id}>
                <td data-label="Time" className="muted mono" style={{fontSize: 11.5, whiteSpace: 'nowrap'}}>{new Date(l.created_at).toLocaleString('en-IN', { hour:'2-digit', minute:'2-digit', day:'2-digit', month:'short' })}</td>
                {admin && <td data-label="User" className="muted" style={{fontSize: 12}}>{l.email || '—'}</td>}
                <td data-label="Endpoint" style={{textTransform:'capitalize'}}>{l.endpoint}</td>
                <td data-label="Alias" className="mono">{l.model_alias || '—'}</td>
                {admin && <td data-label="Provider" className="muted" style={{textTransform:'capitalize'}}>{l.provider || '—'}</td>}
                <td data-label="Tokens" className="mono" style={{textAlign:'right'}}>{(l.input_tokens || 0) + (l.output_tokens || 0)}</td>
                <td data-label="Cost" className="mono" style={{textAlign:'right'}}>{U.rupees(l.cost_paise || 0)}</td>
                <td data-label="Latency" className="muted mono" style={{textAlign:'right'}}>{l.latency_ms || 0}ms</td>
                <td data-label="Status"><span className={'badge ' + (l.status === 'success' ? 'pos' : 'neg')}>{l.status === 'success' ? 'OK' : 'FAIL'}</span></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ───────── User Skills page ─────────
function UserSkills() {
  const { data: skillsList, reload } = useLoad(() => window.HDR_API.skillsLibrary(), []);
  const [open, setOpen] = uSt(false);
  const [editing, setEditing] = uSt(null);
  const [aiBusy, setAiBusy] = uSt(false);
  const toast = useToast();

  const startCreate = () => {
    setEditing({ slug: '', name: '', description: '', system_prompt: '', category: '', is_active: true });
    setOpen(true);
  };

  const startEdit = async (s) => {
    if (!s.is_mine) { toast('Global skills can only be browsed', 'neg'); return; }
    const full = await window.HDR_API.getUserSkill(s.id);
    setEditing(full);
    setOpen(true);
  };

  const save = async () => {
    if (!editing.slug || !editing.name || !editing.description || !editing.system_prompt) {
      toast('Slug, name, description and system prompt required', 'neg'); return;
    }
    try {
      if (editing.id) await window.HDR_API.updateUserSkill(editing.id, editing);
      else {
        const r = await window.HDR_API.createUserSkill(editing);
        if (r.error) throw new Error(r.error);
      }
      toast('Saved — open again to add training data', 'pos');
      setOpen(false); reload();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  const removeSkill = async () => {
    if (!confirm('Delete this skill?')) return;
    await window.HDR_API.deleteUserSkill(editing.id);
    toast('Deleted', 'pos'); setOpen(false); reload();
  };

  const aiSuggest = async () => {
    if (!editing.name) { toast('Enter skill name first', 'neg'); return; }
    setAiBusy(true);
    try {
      const r = await window.HDR_API.aiSuggestUserSkill(editing.name);
      if (r.description) setEditing({
        ...editing,
        description: editing.description || r.description,
        system_prompt: editing.system_prompt || r.system_prompt,
      });
      toast('AI suggested', 'pos');
    } catch (e) { toast('AI suggest failed', 'neg'); }
    finally { setAiBusy(false); }
  };

  const mine   = (skillsList || []).filter(s => s.is_mine);
  const global = (skillsList || []).filter(s => !s.is_mine);

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">My Skills</h1>
          <p className="page-sub">Custom skills define expertise areas (GST advisor, Hinglish writer, etc). Attach to agents for specialized AI behavior.</p>
        </div>
        <div className="actions">
          <button className="btn primary" onClick={startCreate}><I.plus/> New skill</button>
        </div>
      </div>

      {/* My skills */}
      <div className="card" style={{marginBottom: 24}}>
        <div className="card-head">
          <div>
            <div className="card-title">Your skills <span className="dim mono">({mine.length})</span></div>
            <div className="card-sub">Custom skills you've created</div>
          </div>
        </div>
        {mine.length === 0 ? (
          <div className="muted" style={{padding: '24px 0', textAlign: 'center', fontSize: 13}}>
            No custom skills yet. Click "New skill" to create one.<br/>
            <span className="dim" style={{fontSize: 12}}>Tip: use AI suggest to generate description + prompt from just the name.</span>
          </div>
        ) : (
          <div className="grid-3" style={{gap: 12}}>
            {mine.map(s => (
              <div key={s.id} className="card" style={{cursor:'pointer', padding: 14}} onClick={() => startEdit(s)}>
                <div className="row" style={{marginBottom: 6}}>
                  <div style={{fontWeight: 600, fontSize: 13.5}}>{s.name}</div>
                  <div className="spacer"/>
                  <span className={'badge ' + (s.is_active ? 'pos' : 'neg')}>{s.is_active ? 'Active' : 'Off'}</span>
                </div>
                <code className="mono muted" style={{fontSize: 11}}>{s.slug}</code>
                <p className="muted" style={{fontSize: 12, lineHeight: 1.4, marginTop: 8, marginBottom: 0}}>
                  {s.description}
                </p>
              </div>
            ))}
          </div>
        )}
      </div>

      {/* Global skills (read-only) */}
      {global.length > 0 && (
        <div className="card">
          <div className="card-head">
            <div>
              <div className="card-title">Built-in skills <span className="dim mono">({global.length})</span></div>
              <div className="card-sub">Provided by Hidrogen — available to all users (read-only)</div>
            </div>
          </div>
          <div className="grid-3" style={{gap: 12}}>
            {global.map(s => (
              <div key={s.id} className="card" style={{padding: 14, opacity: 0.85}}>
                <div className="row" style={{marginBottom: 6}}>
                  <div style={{fontWeight: 600, fontSize: 13.5}}>{s.name}</div>
                  <div className="spacer"/>
                  <span className="badge accent" style={{fontSize: 10}}>built-in</span>
                </div>
                <code className="mono muted" style={{fontSize: 11}}>{s.slug}</code>
                <p className="muted" style={{fontSize: 12, lineHeight: 1.4, marginTop: 8, marginBottom: 0}}>
                  {s.description}
                </p>
              </div>
            ))}
          </div>
        </div>
      )}

      <Modal open={open} onClose={() => setOpen(false)}
             title={editing?.id ? `Edit "${editing?.name}"` : 'Create skill'}
             sub="Skills define expertise areas you can attach to your agents."
             size="wide"
             foot={<>
               {editing?.id && <button className="btn ghost" onClick={removeSkill}><I.trash/> Delete</button>}
               <div className="spacer"/>
               <button className="btn ghost" onClick={() => setOpen(false)}>Cancel</button>
               <button className="btn primary" onClick={save}><I.check/> Save</button>
             </>}>
        {editing && (
          <div className="col" style={{gap: 14}}>
            <div className="row" style={{gap: 12}}>
              <div className="field" style={{flex:1}}>
                <div className="label">Slug</div>
                <input className="input mono" value={editing.slug || ''}
                       onChange={e => setEditing({...editing, slug: e.target.value.toLowerCase().replace(/[^a-z0-9-]/g,'-')})}
                       placeholder="my-skill" disabled={!!editing.id}/>
              </div>
              <div className="field" style={{flex:1}}>
                <div className="label row" style={{justifyContent: 'space-between'}}>
                  <span>Name</span>
                  <button className="btn ghost" style={{padding: '2px 8px', fontSize: 11}} onClick={aiSuggest} disabled={aiBusy}>
                    {aiBusy ? '...' : <><I.spark/> AI suggest</>}
                  </button>
                </div>
                <input className="input" value={editing.name || ''}
                       onChange={e => setEditing({...editing, name: e.target.value})}
                       placeholder="GST Advisor"/>
              </div>
            </div>

            <div className="field">
              <div className="label">Description <span className="dim">(used for AI routing)</span></div>
              <textarea className="textarea" rows={2} value={editing.description || ''}
                        onChange={e => setEditing({...editing, description: e.target.value})}
                        placeholder="Use when user asks about GST, returns, or compliance."/>
            </div>

            <div className="field">
              <div className="label">System prompt</div>
              <textarea className="textarea" rows={6} value={editing.system_prompt || ''}
                        onChange={e => setEditing({...editing, system_prompt: e.target.value})}
                        placeholder="You are a GST expert specializing in Indian tax law..."/>
            </div>

            <div className="row" style={{gap: 12}}>
              <div className="field" style={{flex:1}}>
                <div className="label">Category</div>
                <input className="input" value={editing.category || ''}
                       onChange={e => setEditing({...editing, category: e.target.value})}
                       placeholder="finance"/>
              </div>
              <div className="field">
                <div className="label">Active</div>
                <Switch on={!!editing.is_active} onChange={v => setEditing({...editing, is_active: v})}/>
              </div>
            </div>

            {/* Knowledge training section — only for saved skills */}
            {editing?.id && (
              <div style={{borderTop: '1px solid var(--border)', paddingTop: 18, marginTop: 6}}>
                <div className="card-title" style={{marginBottom: 4}}>
                  <I.book/> Train this skill with data
                </div>
                <div className="card-sub" style={{marginBottom: 14}}>
                  Add knowledge from PDFs, websites, APIs, or pasted text. The agent uses this as context when this skill is active.
                </div>
                <SkillKnowledgeManager skillId={editing.id}/>
              </div>
            )}
            {!editing?.id && (
              <div style={{
                fontSize: 12, padding: 12,
                background: 'var(--accent-soft)',
                border: '1px solid var(--accent-line)',
                borderRadius: 'var(--r-md)',
                lineHeight: 1.5,
              }}>
                <strong>📚 Want to add PDFs, websites, APIs as training data?</strong><br/>
                <span className="muted">
                  Click <strong style={{color:'var(--text)'}}>Save</strong> below — the form will stay open and the training section will appear here.
                </span>
              </div>
            )}
          </div>
        )}
      </Modal>
    </div>
  );
}

// ───────── Skill Knowledge Manager (used inside skill modal) ─────────
function SkillKnowledgeManager({ skillId }) {
  const [items, setItems] = uSt([]);
  const [mode, setMode]   = uSt('text'); // text | pdf | url | api
  const [title, setTitle] = uSt('');
  const [content, setContent] = uSt('');
  const [url, setUrl]     = uSt('');
  const [apiHeaders, setApiHeaders] = uSt('');
  const [refreshHrs, setRefreshHrs] = uSt(24);
  const [pdfFile, setPdfFile] = uSt(null);
  const [busy, setBusy]   = uSt(false);
  const toast = useToast();

  const load = async () => {
    try { setItems(await window.HDR_API.listSkillKnowledge(skillId)); }
    catch { setItems([]); }
  };
  uEf(() => { load(); }, [skillId]);

  const reset = () => { setTitle(''); setContent(''); setUrl(''); setApiHeaders(''); setPdfFile(null); };

  const addKnowledge = async () => {
    setBusy(true);
    try {
      let r;
      if (mode === 'text') {
        if (!content.trim()) throw new Error('Content required');
        r = await window.HDR_API.addSkillKnowledgeText(skillId, { title: title || 'Notes', content });
      } else if (mode === 'pdf') {
        if (!pdfFile) throw new Error('PDF file required');
        r = await window.HDR_API.addSkillKnowledgePdf(skillId, pdfFile, title);
      } else if (mode === 'url') {
        if (!url.trim()) throw new Error('URL required');
        r = await window.HDR_API.addSkillKnowledgeUrl(skillId, { url, title, auto_refresh_hours: refreshHrs });
      } else if (mode === 'api') {
        if (!url.trim()) throw new Error('API endpoint required');
        let headers = {};
        if (apiHeaders.trim()) {
          try { headers = JSON.parse(apiHeaders); }
          catch { throw new Error('Headers must be valid JSON'); }
        }
        r = await window.HDR_API.addSkillKnowledgeApi(skillId, { url, title, headers, auto_refresh_hours: refreshHrs });
      }
      if (r?.error) throw new Error(r.error);
      toast(`Added ${(r.size_bytes/1024).toFixed(1)}KB · ~${r.token_estimate} tokens`, 'pos');
      reset();
      load();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
    finally { setBusy(false); }
  };

  const refresh = async (kid) => {
    try {
      const r = await window.HDR_API.refreshSkillKnowledge(skillId, kid);
      toast(r.changed ? `Updated — ${(r.size_bytes/1024).toFixed(1)}KB` : 'No changes since last fetch', 'pos');
      load();
    } catch (e) { toast('Refresh failed: ' + e.message, 'neg'); }
  };

  const removeEntry = async (kid) => {
    if (!confirm('Remove this knowledge entry?')) return;
    try {
      await window.HDR_API.deleteSkillKnowledge(skillId, kid);
      toast('Removed', 'pos');
      load();
    } catch (e) { toast('Failed: ' + e.message, 'neg'); }
  };

  return (
    <div className="col" style={{gap: 14}}>
      {/* Mode tabs */}
      <div className="seg">
        <button className={mode === 'text' ? 'active' : ''} onClick={() => setMode('text')}>Text</button>
        <button className={mode === 'pdf'  ? 'active' : ''} onClick={() => setMode('pdf')}>PDF</button>
        <button className={mode === 'url'  ? 'active' : ''} onClick={() => setMode('url')}>Website</button>
        <button className={mode === 'api'  ? 'active' : ''} onClick={() => setMode('api')}>External API</button>
      </div>

      {/* Mode-specific input */}
      {mode === 'text' && (
        <>
          <div className="field">
            <div className="label">Title (optional)</div>
            <input className="input" value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. Pricing FAQ"/>
          </div>
          <div className="field">
            <div className="label">Content</div>
            <textarea className="textarea" rows={6} value={content} onChange={e => setContent(e.target.value)}
                      placeholder="Paste documentation, FAQs, notes, etc..."/>
          </div>
        </>
      )}

      {mode === 'pdf' && (
        <>
          <div className="field">
            <div className="label">Title (optional)</div>
            <input className="input" value={title} onChange={e => setTitle(e.target.value)} placeholder="Defaults to filename"/>
          </div>
          <div className="field">
            <div className="label">PDF file (max 10 MB)</div>
            <input type="file" accept="application/pdf,.pdf" className="input"
                   onChange={e => setPdfFile(e.target.files[0] || null)}/>
            {pdfFile && <div className="muted" style={{fontSize: 11.5, marginTop: 6}}>
              {pdfFile.name} · {(pdfFile.size/1024).toFixed(1)}KB
            </div>}
          </div>
        </>
      )}

      {mode === 'url' && (
        <>
          <div className="field">
            <div className="label">Website URL</div>
            <input className="input mono" type="url" value={url} onChange={e => setUrl(e.target.value)}
                   placeholder="https://docs.example.com/pricing"/>
          </div>
          <div className="row" style={{gap: 12}}>
            <div className="field" style={{flex: 2}}>
              <div className="label">Title (optional)</div>
              <input className="input" value={title} onChange={e => setTitle(e.target.value)} placeholder="Pricing page"/>
            </div>
            <div className="field" style={{flex: 1}}>
              <div className="label">Auto-refresh (hrs)</div>
              <input className="input" type="number" min="0" max="720" value={refreshHrs}
                     onChange={e => setRefreshHrs(parseInt(e.target.value, 10) || 0)}/>
            </div>
          </div>
          <div className="muted" style={{fontSize: 11, lineHeight: 1.5}}>
            We'll fetch and strip the page text. Auto-refresh re-fetches when the source changes — agent stays up to date.
          </div>
        </>
      )}

      {mode === 'api' && (
        <>
          <div className="field">
            <div className="label">API endpoint (returns JSON / text)</div>
            <input className="input mono" type="url" value={url} onChange={e => setUrl(e.target.value)}
                   placeholder="https://api.example.com/data"/>
          </div>
          <div className="field">
            <div className="label">Headers (JSON, optional)</div>
            <textarea className="textarea mono" rows={3} value={apiHeaders} onChange={e => setApiHeaders(e.target.value)}
                      placeholder='{"Authorization": "Bearer xxx", "X-API-Key": "yyy"}'
                      style={{fontSize: 12}}/>
          </div>
          <div className="row" style={{gap: 12}}>
            <div className="field" style={{flex: 2}}>
              <div className="label">Title</div>
              <input className="input" value={title} onChange={e => setTitle(e.target.value)} placeholder="Inventory API"/>
            </div>
            <div className="field" style={{flex: 1}}>
              <div className="label">Refresh (hrs)</div>
              <input className="input" type="number" min="0" max="720" value={refreshHrs}
                     onChange={e => setRefreshHrs(parseInt(e.target.value, 10) || 0)}/>
            </div>
          </div>
          <div className="muted" style={{fontSize: 11, lineHeight: 1.5}}>
            We'll re-poll this API and update knowledge. Useful when the source data changes frequently (prices, inventory, schedules).
          </div>
        </>
      )}

      <button className="btn primary sm" onClick={addKnowledge} disabled={busy}>
        <I.plus/> {busy ? 'Adding…' : 'Add to training'}
      </button>

      {/* List existing entries */}
      {items.length > 0 && (
        <div>
          <div className="card-title" style={{fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)', marginBottom: 8, marginTop: 8}}>
            Training data ({items.length})
          </div>
          <div className="col" style={{gap: 6}}>
            {items.map(k => (
              <div key={k.id} className="row" style={{
                padding: '8px 12px', background: 'var(--surface-2)',
                border: '1px solid var(--border)', borderRadius: 'var(--r-md)', gap: 10,
              }}>
                <div style={{flex: 1, minWidth: 0}}>
                  <div className="row" style={{gap: 6, marginBottom: 2}}>
                    <span style={{fontWeight: 500, fontSize: 12.5, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{k.title}</span>
                    <span className="badge" style={{fontSize: 9.5, textTransform:'uppercase'}}>{k.source_type || 'text'}</span>
                  </div>
                  <div className="muted mono" style={{fontSize: 10.5}}>
                    {(k.size_bytes/1024).toFixed(1)}KB · ~{k.token_estimate} tokens
                    {k.last_fetched_at && ` · refreshed ${U.ago(new Date(k.last_fetched_at).getTime())}`}
                  </div>
                </div>
                {(k.source_type === 'url' || k.source_type === 'api') && (
                  <button className="icon-btn" title="Refresh now" onClick={() => refresh(k.id)}>
                    <I.refresh size={14}/>
                  </button>
                )}
                <button className="icon-btn" title="Remove" onClick={() => removeEntry(k.id)}>
                  <I.trash size={14}/>
                </button>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ───────── Docs (split into Text / Image / Voice / STT) ─────────
function UserDocs() {
  const [tab, setTab] = uSt('text');
  const toast = useToast();

  const tabs = [
    { id: 'text',  label: 'Text / Chat', icon: <I.code/> },
    { id: 'image', label: 'Image',       icon: <I.spark/> },
    { id: 'voice', label: 'Voice (TTS)', icon: <I.flask/> },
    { id: 'stt',   label: 'Speech-to-Text', icon: <I.list/> },
    { id: 'live',  label: 'Live Call',   icon: <I.zap/> },
  ];

  return (
    <div>
      <div className="page-head">
        <div>
          <h1 className="page-title">Docs</h1>
          <p className="page-sub">Simple JSON REST API. Works with any HTTP client — no SDK required.</p>
        </div>
      </div>

      {/* Endpoint type tabs */}
      <div className="card" style={{padding: 0, marginBottom: 18}}>
        <div className="row" style={{padding: '0 12px', borderBottom: '1px solid var(--border)', gap: 0, flexWrap: 'wrap'}}>
          {tabs.map(t => (
            <button key={t.id}
                    className={'code-tab' + (tab === t.id ? ' active' : '')}
                    style={{display: 'flex', alignItems:'center', gap: 6}}
                    onClick={() => setTab(t.id)}>
              {t.icon} {t.label}
            </button>
          ))}
        </div>
        <div style={{padding: '20px 20px 24px'}}>
          {tab === 'text'  && <DocsText  toast={toast}/>}
          {tab === 'image' && <DocsImage toast={toast}/>}
          {tab === 'voice' && <DocsVoice toast={toast}/>}
          {tab === 'stt'   && <DocsStt   toast={toast}/>}
          {tab === 'live'  && <DocsLive  toast={toast}/>}
        </div>
      </div>

      {/* Agent + model alias reference (shared across all tabs) */}
      <div className="grid-23" style={{gap: 16}}>
        <div className="card">
          <div className="card-head"><div className="card-title">Model aliases</div></div>
          <table className="table">
            <thead><tr><th>Alias</th><th>Use case</th></tr></thead>
            <tbody>
              {C.aliases.map(a => (
                <tr key={a.slug}>
                  <td className="mono">{a.slug}</td>
                  <td className="muted">{a.desc}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div className="card">
          <div className="card-head"><div className="card-title">Use an agent</div></div>
          <p className="muted" style={{fontSize: 13, lineHeight: 1.55}}>
            Add <span className="mono">"agent": "your-slug"</span> to any chat request. The agent's
            attached skills + tools auto-apply.
          </p>
          <pre className="code">{`{
  "agent": "mera-ca",
  "messages": [
    {"role":"user","content":"GSTIN 27AAACC1234A1Z5"}
  ]
}`}</pre>
        </div>
      </div>
    </div>
  );
}

function DocSection({ title, sub, endpoint, method, params, example, toast }) {
  return (
    <div>
      <div className="row" style={{marginBottom: 18, gap: 14}}>
        <div>
          <h3 style={{margin: '0 0 4px', fontSize: 18, fontWeight: 500}}>{title}</h3>
          <p className="muted" style={{margin: 0, fontSize: 13.5}}>{sub}</p>
        </div>
      </div>

      <div className="row" style={{marginBottom: 18, padding: '10px 14px', background: 'var(--surface-2)', borderRadius: 'var(--r-md)', fontSize: 13}}>
        <span className="badge accent">{method}</span>
        <span className="mono">{endpoint}</span>
      </div>

      <div style={{marginBottom: 20}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Parameters</div>
        <table className="table">
          <thead><tr><th>Field</th><th>Type</th><th>Description</th><th>Default</th></tr></thead>
          <tbody>
            {params.map(p => (
              <tr key={p.name}>
                <td className="mono">{p.name}{p.required && <span style={{color:'var(--neg)'}}>*</span>}</td>
                <td className="muted mono" style={{fontSize: 12}}>{p.type}</td>
                <td className="muted" style={{fontSize: 12.5}}>{p.desc}</td>
                <td className="muted mono" style={{fontSize: 12}}>{p.default || '—'}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Example</div>
        <div style={{position: 'relative'}}>
          <button className="icon-btn" style={{position:'absolute', top:8, right:8, zIndex: 1}}
                  onClick={() => { navigator.clipboard.writeText(example); toast('Copied', 'pos'); }}>
            <I.copy/>
          </button>
          <pre className="code-block">{example}</pre>
        </div>
      </div>
    </div>
  );
}

function DocsText({ toast }) {
  return <DocSection
    toast={toast}
    title="Text / Chat completion"
    sub="Hidrogen's chat completion endpoint. Send messages, get back a response. Use agent slug for skill-aware behavior."
    method="POST"
    endpoint="https://api.hidrogen.in/v1/chat/completions"
    params={[
      { name: 'model', type: 'string', required: true, desc: 'Model alias: smart, smart-pro, fast, creative', default: 'smart' },
      { name: 'messages', type: 'array', required: true, desc: 'Conversation history with role + content', default: '—' },
      { name: 'agent', type: 'string', required: false, desc: 'Your agent slug for skills + tools', default: '—' },
      { name: 'temperature', type: 'number', required: false, desc: '0.0 (deterministic) to 2.0 (creative)', default: '0.7' },
      { name: 'max_tokens', type: 'integer', required: false, desc: 'Max response length', default: '2048' },
      { name: 'top_p', type: 'number', required: false, desc: 'Nucleus sampling cutoff', default: '1.0' },
      { name: 'stop', type: 'array', required: false, desc: 'Stop sequences (strings)', default: 'null' },
      { name: 'response_format', type: 'object', required: false, desc: '{type: "json_object"} for JSON mode', default: 'null' },
      { name: 'buttons', type: 'boolean', required: false, desc: 'Get 3 suggested follow-up buttons in response.buttons', default: 'false' },
      { name: 'decision', type: 'boolean', required: false, desc: 'Get next-action hint in response.decision {action, reason, next_step, confidence}', default: 'false' },
    ]}
    example={`curl https://api.hidrogen.in/v1/chat/completions \\
  -H "Authorization: Bearer hi_xxx" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "smart",
    "messages": [
      {"role": "user", "content": "Hello!"}
    ],
    "temperature": 0.7,
    "max_tokens": 1024
  }'`}
  />;
}

function DocsImage({ toast }) {
  return <DocSection
    toast={toast}
    title="Image generation"
    sub="Generate images from a text prompt. Returns base64 or URL based on response_format."
    method="POST"
    endpoint="https://api.hidrogen.in/v1/images/generations"
    params={[
      { name: 'prompt', type: 'string', required: true, desc: 'Description of the image to generate', default: '—' },
      { name: 'model', type: 'string', required: false, desc: 'Alias: image', default: 'image' },
      { name: 'n', type: 'integer', required: false, desc: 'How many images (1-4)', default: '1' },
      { name: 'size', type: 'string', required: false, desc: '1024x1024, 512x512, 1792x1024, etc.', default: '1024x1024' },
      { name: 'quality', type: 'string', required: false, desc: 'standard | hd', default: 'standard' },
      { name: 'style', type: 'string', required: false, desc: 'natural | vivid', default: 'natural' },
    ]}
    example={`curl https://api.hidrogen.in/v1/images/generations \\
  -H "Authorization: Bearer hi_xxx" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "image",
    "prompt": "A sunset over the Taj Mahal, photorealistic",
    "n": 1,
    "size": "1024x1024"
  }'

# Response:
# { "data": [{ "b64_json": "iVBORw0KG..." }] }`}
  />;
}

function DocsVoice({ toast }) {
  return <DocSection
    toast={toast}
    title="Text-to-Speech (TTS)"
    sub="Convert text to natural-sounding speech. Use 'voice' alias for English, supports multiple voices."
    method="POST"
    endpoint="https://api.hidrogen.in/v1/audio/speech"
    params={[
      { name: 'input', type: 'string', required: true, desc: 'Text to convert to speech (max 4096 chars)', default: '—' },
      { name: 'model', type: 'string', required: false, desc: 'Alias: voice', default: 'voice' },
      { name: 'voice', type: 'string', required: false, desc: 'Voice name (priya, arjun, kavya, dev, meera, raj)', default: 'priya' },
      { name: 'language', type: 'string', required: false, desc: 'Language hint (en, hi, ta, te, etc.)', default: 'en' },
      { name: 'speed', type: 'number', required: false, desc: 'Playback speed (0.25 - 4.0)', default: '1.0' },
      { name: 'response_format', type: 'string', required: false, desc: 'mp3 | wav | opus', default: 'mp3' },
    ]}
    example={`curl https://api.hidrogen.in/v1/audio/speech \\
  -H "Authorization: Bearer hi_xxx" \\
  -H "Content-Type: application/json" \\
  -d '{
    "model": "voice",
    "input": "Namaste! Hidrogen me aapka swagat hai.",
    "voice": "priya",
    "language": "hi"
  }' \\
  --output speech.mp3

# Returns audio file (binary)`}
  />;
}

function DocsStt({ toast }) {
  return <DocSection
    toast={toast}
    title="Speech-to-Text (STT)"
    sub="Transcribe audio to text. Use 'stt-in' alias for Indian languages (Hindi, Tamil, Telugu, Marathi, etc.) or 'stt' for English/global."
    method="POST (multipart/form-data)"
    endpoint="https://api.hidrogen.in/v1/audio/transcriptions"
    params={[
      { name: 'file', type: 'binary', required: true, desc: 'Audio file (.mp3, .wav, .m4a, .ogg)', default: '—' },
      { name: 'model', type: 'string', required: false, desc: 'Alias: stt | stt-in', default: 'stt' },
      { name: 'language', type: 'string', required: false, desc: 'Language code (en, hi, ta, etc.)', default: 'auto' },
      { name: 'prompt', type: 'string', required: false, desc: 'Hint for context (improves accuracy)', default: '—' },
      { name: 'response_format', type: 'string', required: false, desc: 'json | text | srt | vtt', default: 'json' },
    ]}
    example={`curl https://api.hidrogen.in/v1/audio/transcriptions \\
  -H "Authorization: Bearer hi_xxx" \\
  -F "file=@audio.mp3" \\
  -F "model=stt-in" \\
  -F "language=hi"

# Response:
# { "text": "Namaste, Hidrogen me aapka swagat hai." }`}
  />;
}

// ───────── Live Call (WebSocket) ─────────
// Custom layout — DocSection is REST-shaped; the Live Call uses a bidirectional
// WS with a JSON control protocol, so we render the docs by hand using the same
// card / code-block / table primitives as the other tabs.
function DocsLive({ toast }) {
  const copy = (text) => { navigator.clipboard.writeText(text); toast('Copied', 'pos'); };

  const wsUrl = 'wss://api.hidrogen.in/v1/live';

  const connectParams = [
    {
      name: 'key', type: 'string', required: 'either',
      desc: 'Hidrogen API key (prefix hi_ or sk_). Pass either as ?key=... in the URL OR as an Authorization: Bearer header — pick exactly one. Browsers can only set query params, so the dashboard tester uses ?key=; GSM gateways and server-side clients should prefer the header, which avoids leaking the key into nginx access logs.',
      default: '—',
    },
    {
      name: 'input_sample_rate', type: 'integer', required: false,
      desc: 'Sample rate (in Hz) of the PCM16 mono audio frames YOU send to the server. Allowed: 8000, 16000, 24000, 48000. Use 8000 for GSM/SIP/telephony bridges — Hidrogen will up-sample to Gemini\'s native 16 kHz transparently. Use 48000 if you\'re feeding raw browser-mic samples without client-side resampling. Anything else and the server resamples.',
      default: '16000',
    },
    {
      name: 'output_sample_rate', type: 'integer', required: false,
      desc: 'Sample rate (in Hz) you want the AI audio chunks delivered at. Allowed: 8000, 16000, 24000, 48000. Gemini emits at 24 kHz natively; the server resamples to whatever you ask. Use 8000 if you\'re writing the reply straight to a phone line, 24000 (default) for high-quality browser/app playback.',
      default: '24000',
    },
    {
      name: 'system_prompt', type: 'string', required: false,
      desc: 'URL-encoded system instruction that defines the AI\'s persona, language, and behavior for the entire call. Examples: "You are a courteous customer-support agent. Reply in Hindi unless the caller speaks English." Capped at 4000 chars. Sent to the model exactly once at session start; cannot be changed mid-call.',
      default: '—',
    },
    {
      name: 'greet', type: 'string', required: false,
      desc: 'Make the AI speak the first turn. Two modes: pass "1" for a sensible default opener ("Greet the user warmly and briefly. Ask how you can help."), or pass any URL-encoded directive of your own ("Namaste! Briefly introduce yourself in Hinglish."). The text is sent as a hidden user turn — Gemini responds in audio so the caller hears the greeting the moment the line opens. Capped at 1000 chars. Counts as a billed turn (same per-token pricing as any other turn).',
      default: 'off (AI waits for caller audio)',
    },
  ];

  const protocol = [
    {
      dir: '← server',
      frame: '{"type":"ready"}',
      meaning: 'Sent once, after Gemini accepts the setup. From this point you may send audio. If you opened with ?greet=…, the AI will start emitting "audio" frames immediately after this — be ready to play.',
    },
    {
      dir: '→ client',
      frame: '{"type":"audio","data":"<base64 PCM16>"}',
      meaning: 'Microphone / telephony audio chunk. PCM16 mono at the input_sample_rate you connected with, base64-encoded. Recommended chunk size: ~20–100 ms of audio (avoid mega-frames; the server forwards each one to Gemini as it arrives).',
    },
    {
      dir: '→ client',
      frame: '{"type":"end"}',
      meaning: 'Explicit end-of-utterance signal. The server VAD already detects end-of-speech automatically, so this is only useful for pipelines without natural pauses (e.g. you\'re concatenating recorded clips). Safe to omit.',
    },
    {
      dir: '→ client',
      frame: '{"type":"text","text":"..."}',
      meaning: 'Send a text message in the middle of a voice call (e.g. inject a quick instruction from your back-end logic). The model replies with audio as usual. Doesn\'t replace ongoing audio input — both streams coexist.',
    },
    {
      dir: '← server',
      frame: '{"type":"audio","data":"<base64 PCM16>"}',
      meaning: 'AI voice reply chunk. PCM16 mono at the output_sample_rate you requested. Append directly to your speaker / phone-line buffer; chunks arrive in playback order.',
    },
    {
      dir: '← server',
      frame: '{"type":"interrupted"}',
      meaning: 'The caller spoke while the AI was speaking ("barge-in"). The AI has stopped emitting audio for the current turn — flush every queued/scheduled audio buffer on your side immediately so the AI\'s tail doesn\'t bleed over the new question.',
    },
    {
      dir: '← server',
      frame: '{"type":"turn_complete"}',
      meaning: 'The AI finished its current turn — no more audio frames coming until the caller speaks again. Use this to mute "AI speaking" indicators in your UI.',
    },
    {
      dir: '← server',
      frame: '{"type":"ending","reason":"..."}',
      meaning: 'Call is about to close (the server closes the socket within a few hundred ms after this). reason ∈ {max_duration, insufficient_balance, server_goaway, upstream_closed, idle_timeout}. Final billing is settled before the close.',
    },
    {
      dir: '← server',
      frame: '{"type":"error","code":"...","message":"..."}',
      meaning: 'Fatal error — the socket will close right after this frame. code ∈ {auth, insufficient_balance, upstream, server}. Inspect message for the human-readable cause.',
    },
  ];

  // Close-code reference — emitted on the WS close event.
  const closeCodes = [
    { code: '1000', label: 'normal',               desc: 'Clean close. Either side asked to end the call, or the upstream session reached its natural end.' },
    { code: '4401', label: 'auth',                 desc: 'API key missing, malformed, revoked, or belongs to a deactivated user.' },
    { code: '4402', label: 'insufficient_balance', desc: 'Wallet balance fell below min_balance_paise (set in Admin → Settings). Top up and reconnect.' },
    { code: '4408', label: 'timeout',              desc: 'Either no caller audio for 60 s (idle) or the 15-minute hard session cap (max_duration).' },
    { code: '4502', label: 'upstream_error',       desc: 'Gemini Live API rejected the session, refused the model ID, or dropped the connection mid-call.' },
  ];

  const nodeExample = `// npm i ws
// Streams a raw PCM16 16 kHz mono file to Hidrogen Live and saves the reply.
const WebSocket = require('ws');
const fs        = require('fs');

const API_KEY = 'hi_xxx';   // your Hidrogen API key

// Optional: make the AI speak first. Pass "1" for a default opener, or any
// custom directive — the model treats it as a hidden user turn and replies in audio.
const greet = encodeURIComponent('Greet the user warmly in one short sentence, then ask how you can help.');

const ws = new WebSocket(
  '${wsUrl}?key=' + API_KEY +
  '&input_sample_rate=16000&output_sample_rate=24000' +
  '&greet=' + greet
);

ws.on('open', () => console.log('connecting…'));

const out = fs.createWriteStream('reply-24k.pcm');

ws.on('message', (raw) => {
  let m; try { m = JSON.parse(raw.toString()); } catch { return; }

  if (m.type === 'ready') {
    // setup done — stream a PCM16 file in ~100 ms chunks (3200 bytes @ 16 kHz)
    const pcm = fs.readFileSync('mic-16k-mono.pcm');
    for (let i = 0; i < pcm.length; i += 3200) {
      const chunk = pcm.subarray(i, i + 3200);
      ws.send(JSON.stringify({ type: 'audio', data: chunk.toString('base64') }));
    }
    ws.send(JSON.stringify({ type: 'end' }));
  }
  if (m.type === 'audio')         out.write(Buffer.from(m.data, 'base64'));
  if (m.type === 'interrupted')   console.log('barge-in — flush playback');
  if (m.type === 'turn_complete') console.log('AI finished turn');
  if (m.type === 'ending')        console.log('call ending:', m.reason);
  if (m.type === 'error')         console.error('error:', m.code, m.message);
});

ws.on('close', (code) => { out.end(); console.log('closed', code); });`;

  const gsmExample = `// GSM / telephony gateway — connect at 8 kHz, Hidrogen resamples both ways.
// Use the Authorization header from non-browser clients to keep the key out of URLs/logs.
// "greet" makes the AI speak first so the caller hears a greeting the moment the line opens.
const url = new URL('${wsUrl}');
url.searchParams.set('input_sample_rate',  '8000');
url.searchParams.set('output_sample_rate', '8000');
url.searchParams.set('greet', 'Namaste! Hidrogen support me aapka swagat hai. Aap kaise sahayata chahte hain?');

const ws = new WebSocket(url.toString(), {
  headers: { Authorization: 'Bearer hi_xxx' },
});

// On {type:'ready'}, pipe inbound phone audio (PCM16 8 kHz mono) as base64 JSON frames.
// On {type:'audio'}, decode base64 PCM16 8 kHz and write to the phone line.`;

  return (
    <div>
      <div className="row" style={{marginBottom: 18, gap: 14}}>
        <div>
          <h3 style={{margin: '0 0 4px', fontSize: 18, fontWeight: 500}}>Live Call (real-time voice)</h3>
          <p className="muted" style={{margin: 0, fontSize: 13.5}}>
            Bidirectional voice WebSocket — talk to the AI, hear it reply in real time.
            Connect any audio source: a browser mic, a mobile app, or a <strong style={{color:'var(--text)'}}>GSM/telephony gateway</strong> bridging a phone call.
          </p>
        </div>
      </div>

      {/* Endpoint badge row */}
      <div className="row" style={{marginBottom: 18, padding: '10px 14px', background: 'var(--surface-2)', borderRadius: 'var(--r-md)', fontSize: 13}}>
        <span className="badge accent">WSS</span>
        <span className="mono">{wsUrl}</span>
      </div>

      {/* Authentication */}
      <div style={{marginBottom: 20}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Authentication</div>
        <p className="muted" style={{fontSize: 13, lineHeight: 1.6, marginTop: 0}}>
          Every connection requires a Hidrogen API key (the same <span className="mono">hi_…</span> keys you use for the REST endpoints). Choose one of two delivery methods — never both at once:
        </p>
        <ul style={{margin: '0 0 8px 0', paddingLeft: 18, fontSize: 13, lineHeight: 1.75}}>
          <li>
            <strong>HTTP header</strong> <span className="mono">Authorization: Bearer hi_xxx</span> — the recommended method for any server-side or GSM/telephony client. The key never appears in URLs or proxy logs, and the connection is indistinguishable from a normal REST call to security tooling.
          </li>
          <li>
            <strong>Query parameter</strong> <span className="mono">?key=hi_xxx</span> — the only option for browser clients, since the WebSocket API doesn't expose request headers. The key will appear in nginx access logs unless you scrub it; rotate keys regularly if browser auth is your main path.
          </li>
        </ul>
        <p className="muted" style={{fontSize: 12.5, lineHeight: 1.55, margin: '8px 0 0'}}>
          Authentication failures close the socket with code <span className="mono">4401</span>. Successful connections share the same key cache as the REST endpoints, so a key already warm from <span className="mono">/v1/chat/completions</span> is a cache hit here too.
        </p>
      </div>

      {/* Connect params */}
      <div style={{marginBottom: 20}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Connect parameters (query string)</div>
        <table className="table">
          <thead><tr><th>Field</th><th>Type</th><th>Description</th><th>Default</th></tr></thead>
          <tbody>
            {connectParams.map(p => (
              <tr key={p.name}>
                <td className="mono">{p.name}{p.required === true && <span style={{color:'var(--neg)'}}>*</span>}{p.required === 'either' && <span className="dim" style={{fontSize:10.5, marginLeft:4}}>(or header)</span>}</td>
                <td className="muted mono" style={{fontSize: 12}}>{p.type}</td>
                <td className="muted" style={{fontSize: 12.5}}>{p.desc}</td>
                <td className="muted mono" style={{fontSize: 12}}>{p.default}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* Audio format */}
      <div style={{marginBottom: 20}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Audio format</div>
        <p className="muted" style={{fontSize: 13, lineHeight: 1.65, margin: '0 0 8px'}}>
          All audio — both directions — is <strong style={{color:'var(--text)'}}>16-bit signed little-endian PCM, single-channel (mono), base64-encoded</strong> inside the JSON frames described below. No headers, no containers, no compression — just raw samples. Send chunks as fast as you produce them (typically 20–100 ms per chunk); the server forwards each one to Gemini immediately.
        </p>
        <p className="muted" style={{fontSize: 13, lineHeight: 1.65, margin: '0 0 8px'}}>
          Default sample rates: <strong style={{color:'var(--text)'}}>16 kHz</strong> for what you send (matches Gemini\'s native input), <strong style={{color:'var(--text)'}}>24 kHz</strong> for what you receive (matches Gemini\'s native output). Both are overridable via the <span className="mono">input_sample_rate</span> / <span className="mono">output_sample_rate</span> query parameters; the server resamples transparently using linear interpolation.
        </p>
        <p className="muted" style={{fontSize: 13, lineHeight: 1.65, margin: '0 0 8px'}}>
          <strong style={{color:'var(--text)'}}>For GSM / SIP / telephony bridges:</strong> set both to <span className="mono">8000</span>. Your bridge can pipe phone-line PCM straight in and write the AI\'s reply straight back to the call without doing any sample-rate conversion of its own.
        </p>
        <p className="muted" style={{fontSize: 12.5, lineHeight: 1.55, margin: 0}}>
          Allowed values: <span className="mono">8000</span>, <span className="mono">16000</span>, <span className="mono">24000</span>, <span className="mono">48000</span>. Anything outside that list is rejected with close code <span className="mono">4400</span>.
        </p>
      </div>

      {/* Protocol */}
      <div style={{marginBottom: 20}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>JSON message protocol</div>
        <p className="muted" style={{fontSize: 12.5, lineHeight: 1.6, margin: '0 0 10px'}}>
          Every frame in both directions is a UTF-8 JSON text frame (no binary WebSocket frames are used). The <span className="mono">type</span> field discriminates the shape; unknown types are ignored. Frames are processed in arrival order with no buffering on either side.
        </p>
        <table className="table">
          <thead><tr><th style={{whiteSpace:'nowrap'}}>Direction</th><th>Frame</th><th>Meaning</th></tr></thead>
          <tbody>
            {protocol.map((p, i) => (
              <tr key={i}>
                <td className="muted mono" style={{fontSize: 11.5, whiteSpace: 'nowrap'}}>{p.dir}</td>
                <td className="mono" style={{fontSize: 12}}>{p.frame}</td>
                <td className="muted" style={{fontSize: 12.5, lineHeight: 1.55}}>{p.meaning}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* WebSocket close codes */}
      <div style={{marginBottom: 20}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>WebSocket close codes</div>
        <p className="muted" style={{fontSize: 12.5, lineHeight: 1.6, margin: '0 0 10px'}}>
          When the server closes the socket, the <span className="mono">close</span> event carries a numeric code. Use it to distinguish "the call ended normally" from "fix something and retry":
        </p>
        <table className="table">
          <thead><tr><th>Code</th><th>Label</th><th>Description</th></tr></thead>
          <tbody>
            {closeCodes.map(c => (
              <tr key={c.code}>
                <td className="mono">{c.code}</td>
                <td className="muted mono" style={{fontSize: 12}}>{c.label}</td>
                <td className="muted" style={{fontSize: 12.5, lineHeight: 1.55}}>{c.desc}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* Node example */}
      <div style={{marginBottom: 16}}>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Example — Node.js client</div>
        <div style={{position: 'relative'}}>
          <button className="icon-btn" style={{position:'absolute', top:8, right:8, zIndex: 1}}
                  onClick={() => copy(nodeExample)}>
            <I.copy/>
          </button>
          <pre className="code-block">{nodeExample}</pre>
        </div>
      </div>

      {/* GSM example */}
      <div>
        <div className="card-title" style={{marginBottom: 10, fontSize: 12.5, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)'}}>Example — GSM / telephony bridge</div>
        <div style={{position: 'relative'}}>
          <button className="icon-btn" style={{position:'absolute', top:8, right:8, zIndex: 1}}
                  onClick={() => copy(gsmExample)}>
            <I.copy/>
          </button>
          <pre className="code-block">{gsmExample}</pre>
        </div>
        <p className="muted" style={{fontSize: 12, lineHeight: 1.55, marginTop: 10}}>
          Most telephony stacks (Asterisk AudioSocket, SIP gateways, SIM800/SIM900 modems via a host program) deliver 8 kHz µ-law or A-law audio. Decode to PCM16 in your bridge process, connect with <span className="mono">input_sample_rate=8000</span> and <span className="mono">output_sample_rate=8000</span>, and Hidrogen handles the 8 ↔ 16/24 kHz resampling so you can write the reply straight back to the phone line.
        </p>
      </div>
    </div>
  );
}
