const { useState, useEffect } = React;

const API = (location.hostname === "localhost" || location.hostname === "127.0.0.1")
  ? "http://localhost:8787"
  : "https://api.kachina.app";

const SECTIONS = [
  ["intro", "Présentation"],
  ["auth", "Authentification"],
  ["agents", "Brancher un agent"],
  ["mcp", "MCP"],
  ["chat", "Chat"],
  ["ingest", "Injection"],
  ["images", "Images"],
  ["documents", "Documents"],
  ["library", "Bibliothèque"],
  ["themes", "Thématiques"],
  ["tasks", "Tâches"],
  ["keys", "Clés API"],
  ["connectors", "Connecteurs métier"],
  ["activity", "Qui / quoi / quand"],
  ["errors", "Erreurs"],
];

function Ep({ method, path }) {
  const cls = { GET: "get", POST: "post", PATCH: "patch", DELETE: "del" }[method] || "get";
  return <div className="ep"><span className={"badge " + cls}>{method}</span><code>{path}</code></div>;
}

function App() {
  const [page, setPage] = useState("intro");
  const [health, setHealth] = useState(null);
  useEffect(() => {
    fetch(API + "/health").then((r) => r.json()).then(setHealth).catch(() => setHealth({ ok: false }));
  }, []);

  return (
    <div className="layout">
      <aside className="sidebar">
        <div className="brand">
          <img src="assets/img/mark.png" alt="" />
          <div><strong>KACHINA</strong><div style={{ fontSize: ".75rem", color: "#6f6456" }}>Docs API</div></div>
        </div>
        <div className="sec">Guide</div>
        {SECTIONS.map(([id, label]) => (
          <div key={id} className={"link" + (page === id ? " active" : "")} onClick={() => setPage(id)}>{label}</div>
        ))}
      </aside>
      <main className="content">
        {page === "intro" && (
          <>
            <h1>Cerveau central</h1>
            <p>Une mémoire unique, partagée par toi et tes agents. Tout ce que tu injectes ici - notes, images, faits métier - devient disponible pour Grok, Claude, ChatGPT, Cursor, et n’importe quel MCP.</p>
            <div className="callout">Base : <code>{API}</code> · santé {health?.ok ? "OK" : "injoignable"}</div>
            <h2>Surfaces</h2>
            <table>
              <thead><tr><th>App</th><th>Rôle</th></tr></thead>
              <tbody>
                <tr><td><code>app.kachina.api</code></td><td>Worker : mémoires, vision, RAG, clés, MCP</td></tr>
                <tr><td><code>app.kachina.my</code></td><td>Chat, bibliothèque, thématiques, clés, connecteurs</td></tr>
                <tr><td><code>app.kachina.docs</code></td><td>Cette documentation</td></tr>
              </tbody>
            </table>
            <p>OpenAPI machine : <code>GET /v1/openapi.json</code></p>
          </>
        )}

        {page === "auth" && (
          <>
            <h1>Authentification</h1>
            <p>Deux modes. Le propriétaire utilise une session cookie. Les agents utilisent une clé <code>kch_…</code>.</p>
            <h2>Session propriétaire</h2>
            <Ep method="POST" path="/v1/auth/login" />
            <pre><code>{`curl -X POST ${API}/v1/auth/login \\
  -H 'content-type: application/json' \\
  -d '{"password":"…"}' -c -`}</code></pre>
            <Ep method="GET" path="/v1/auth/me" />
            <Ep method="POST" path="/v1/auth/logout" />
            <h2>Clé agent</h2>
            <pre><code>Authorization: Bearer kch_…
X-Api-Key: kch_…
X-Agent-Name: Claude</code></pre>
            <p><code>X-Agent-Name</code> est recommandé. Sinon le cerveau déduit l’agent depuis le <code>User-Agent</code> (Claude, Grok, ChatGPT, Cursor, Gemini…).</p>
            <p>Chaque appel authentifié par clé crée une ligne dans <code>connections</code>. Le cerveau sait qui s’est connecté, quand, et sur quel endpoint.</p>
          </>
        )}

        {page === "agents" && (
          <>
            <h1>Brancher un agent</h1>
            <p>Crée une clé dans <em>my</em> (une par agent). Tous lisent et écrivent le même cerveau.</p>
            <h2>Claude / ChatGPT (custom GPT ou connecteur)</h2>
            <pre><code>{`GET  ${API}/v1/search?q=…
POST ${API}/v1/ingest
POST ${API}/v1/chat
GET  ${API}/v1/themes`}</code></pre>
            <p>Header : <code>Authorization: Bearer kch_…</code> et <code>X-Agent-Name: Claude</code> (ou ChatGPT).</p>
            <h2>Grok / xAI</h2>
            <p>Même REST, ou MCP distant (ci-dessous). Ajoute <code>X-Agent-Name: Grok</code>.</p>
            <h2>Cursor</h2>
            <pre><code>{`{
  "mcpServers": {
    "kachina": {
      "url": "${API}/mcp",
      "headers": {
        "Authorization": "Bearer kch_VOTRE_CLE",
        "X-Agent-Name": "Cursor"
      }
    }
  }
}`}</code></pre>
          </>
        )}

        {page === "mcp" && (
          <>
            <h1>Serveur MCP</h1>
            <Ep method="POST" path="/mcp" />
            <p>JSON-RPC 2.0, compatible Claude, Cursor, Grok (MCP distant) et tout client MCP HTTP.</p>
            <h2>Outils</h2>
            <table>
              <thead><tr><th>Outil</th><th>Rôle</th></tr></thead>
              <tbody>
                <tr><td><code>brain_search</code></td><td>Recherche sémantique + texte</td></tr>
                <tr><td><code>brain_remember</code></td><td>Injecter un fait durable</td></tr>
                <tr><td><code>brain_library</code></td><td>Lister les fiches</td></tr>
                <tr><td><code>brain_themes</code></td><td>Aperçu par thématique</td></tr>
                <tr><td><code>brain_activity</code></td><td>Journal des actions</td></tr>
                <tr><td><code>brain_who</code></td><td>Dernières connexions d’agents</td></tr>
                <tr><td><code>brain_connectors</code></td><td>MCP / APIs métier branchés</td></tr>
              </tbody>
            </table>
            <pre><code>{`curl -X POST ${API}/mcp \\
  -H 'authorization: Bearer kch_…' \\
  -H 'x-agent-name: Claude' \\
  -H 'content-type: application/json' \\
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'`}</code></pre>
          </>
        )}

        {page === "chat" && (
          <>
            <h1>Chat</h1>
            <Ep method="POST" path="/v1/chat" />
            <pre><code>{`{
  "message": "Qu’est-ce que je sais sur FoodStudio ?",
  "conversation_id": "cv_…",
  "stream": false,
  "remember": false
}`}</code></pre>
            <p>Le cerveau récupère les mémoires pertinentes (Vectorize + FTS), les injecte dans le prompt, puis répond (Grok si <code>XAI_API_KEY</code>, sinon Workers AI).</p>
            <p><code>stream: true</code> renvoie du SSE : <code>data: {"{"}"delta":"…"{"}"}</code>.</p>
            <Ep method="GET" path="/v1/conversations" />
            <Ep method="GET" path="/v1/conversations/:id" />
          </>
        )}

        {page === "ingest" && (
          <>
            <h1>Injection texte</h1>
            <Ep method="POST" path="/v1/ingest" />
            <pre><code>{`{
  "title": "Marge Helvia Q3",
  "content": "Helvia vise 40 % de marge brute…",
  "theme": "business",
  "kind": "note"
}`}</code></pre>
            <p>Thèmes : <code>general</code>, <code>business</code>, <code>personnes</code>, <code>projets</code>, <code>technique</code>, <code>idees</code>, <code>archives</code>.</p>
          </>
        )}

        {page === "images" && (
          <>
            <h1>Images</h1>
            <Ep method="POST" path="/v1/ingest/image" />
            <p>Multipart <code>file</code>. L’image est stockée dans R2, lue par Grok (vision) ou Llama Vision, puis transformée en fiche : titre, synthèse, faits, entités, thème.</p>
            <pre><code>{`curl -X POST ${API}/v1/ingest/image \\
  -H 'authorization: Bearer kch_…' \\
  -F file=@photo.jpg`}</code></pre>
            <Ep method="GET" path="/v1/assets/:id" />
          </>
        )}

        {page === "documents" && (
          <>
            <h1>Documents</h1>
            <Ep method="POST" path="/v1/ingest/document" />
            <p>Multipart <code>file</code>. PDF, Word (<code>.docx</code>, <code>.doc</code>) ou OpenDocument. Le texte est extrait, synthétisé, puis rangé en fiche.</p>
            <pre><code>{`curl -X POST ${API}/v1/ingest/document \\
  -H 'authorization: Bearer kch_…' \\
  -F file=@contrat.pdf`}</code></pre>
            <p>Les <code>.doc</code> anciens marchent mal : mieux vaut un <code>.docx</code> ou un PDF.</p>
          </>
        )}

        {page === "library" && (
          <>
            <h1>Bibliothèque</h1>
            <Ep method="GET" path="/v1/memories?theme=business&q=helvia&limit=40" />
            <Ep method="GET" path="/v1/memories?favorite=1" />
            <Ep method="GET" path="/v1/memories/:id" />
            <Ep method="POST" path="/v1/memories/:id/favorite" />
            <pre><code>{`{ "favorite": true }`}</code></pre>
            <p>Sans body, le cœur bascule. Les favoris remontent en tête de bibliothèque.</p>
            <Ep method="PATCH" path="/v1/memories/:id" />
            <Ep method="DELETE" path="/v1/memories/:id" />
            <Ep method="GET" path="/v1/search?q=…" />
            <Ep method="GET" path="/v1/library" />
          </>
        )}

        {page === "themes" && (
          <>
            <h1>Thématiques</h1>
            <Ep method="GET" path="/v1/themes" />
            <p>Chaque thème expose un compteur et la date de dernière fiche. C’est l’aperçu « bibliothèque par rayon ».</p>
          </>
        )}

        {page === "tasks" && (
          <>
            <h1>Tâches</h1>
            <p>Liste à faire, séparée de la mémoire. Le cerveau les voit dans le chat. Les agents les gèrent via MCP.</p>
            <Ep method="GET" path="/v1/tasks?status=open|done|all" />
            <Ep method="POST" path="/v1/tasks" />
            <pre><code>{`{ "title": "Relancer le devis FoodStudio" }`}</code></pre>
            <Ep method="PATCH" path="/v1/tasks/:id" />
            <pre><code>{`{ "status": "done" }`}</code></pre>
            <Ep method="DELETE" path="/v1/tasks/:id" />
            <p>MCP : <code>brain_tasks</code>, <code>brain_task_add</code>, <code>brain_task_done</code>.</p>
          </>
        )}

        {page === "keys" && (
          <>
            <h1>Clés API</h1>
            <p>Réservé au propriétaire (session).</p>
            <Ep method="GET" path="/v1/keys" />
            <Ep method="POST" path="/v1/keys" />
            <pre><code>{`{ "name": "Claude", "agent_hint": "Claude", "scopes": "read,write,chat,mcp" }`}</code></pre>
            <p>La réponse contient <code>token</code> une seule fois. Stocke-le hors du chat.</p>
            <Ep method="DELETE" path="/v1/keys/:id" />
            <p>Scopes : <code>read</code>, <code>write</code>, <code>chat</code>, <code>mcp</code>. Le propriétaire a <code>*</code>.</p>
          </>
        )}

        {page === "connectors" && (
          <>
            <h1>Connecteurs métier</h1>
            <p>Ajoute un MCP ou une API HTTP vers un business. Le cerveau ping, journalise, et expose la liste aux agents via <code>brain_connectors</code>.</p>
            <Ep method="GET" path="/v1/connectors" />
            <Ep method="POST" path="/v1/connectors" />
            <pre><code>{`{
  "name": "FoodStudio MCP",
  "kind": "mcp",
  "business": "FoodStudio",
  "url": "https://mcp.foodstudio.app/super",
  "auth_type": "bearer",
  "secret": "fsak_…"
}`}</code></pre>
            <Ep method="POST" path="/v1/connectors/:id/ping" />
            <Ep method="PATCH" path="/v1/connectors/:id" />
            <Ep method="DELETE" path="/v1/connectors/:id" />
          </>
        )}

        {page === "activity" && (
          <>
            <h1>Qui / quoi / quand</h1>
            <Ep method="GET" path="/v1/agents" />
            <p>Connexions d’agents : nom, IP, chemin, horodatage, plus un résumé par agent.</p>
            <Ep method="GET" path="/v1/activity" />
            <p>Journal : login, ingest, chat, ping connecteur, création de clé, oubli de fiche…</p>
          </>
        )}

        {page === "errors" && (
          <>
            <h1>Erreurs</h1>
            <table>
              <thead><tr><th>Code</th><th>error</th><th>Sens</th></tr></thead>
              <tbody>
                <tr><td>401</td><td><code>auth_required</code> / <code>invalid_password</code></td><td>Pas de session ni clé valide</td></tr>
                <tr><td>403</td><td><code>forbidden</code></td><td>Scope insuffisant</td></tr>
                <tr><td>404</td><td><code>not_found</code></td><td>Fiche ou connecteur absent</td></tr>
                <tr><td>413</td><td><code>file_too_large</code></td><td>Image &gt; 20 Mo · PDF &gt; 32 Mo</td></tr>
                <tr><td>500</td><td><code>internal</code></td><td>Voir <code>message</code></td></tr>
              </tbody>
            </table>
            <p>Santé publique : <code>GET /health</code>.</p>
          </>
        )}
      </main>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("app")).render(<App />);
