import { APP_PLATFORM_CSP_RESOURCE_DOMAINS } from "@/services/apps/app-ui-policy";

// The escalation pointer appended to the app-authoring tool descriptions
// (scaffold_app / refine_app / edit_app), in one place so the three cannot
// drift. Names what the inline SDK summary omits so the model knows when to
// load the full Build App skill.
export const BUILD_APP_SKILL_POINTER =
  'For tool-calling apps (the assign→preview→diagnostics build loop), the CDN allowlist, or platform theming, load the "Build App" skill (in your available skills) for the full authoring playbook.';

// The recurring "no render has happened yet, and that is fine" directive shared
// by validate_app / get_app_diagnostics result text, so the guidance stays
// identical wherever the model reads it.
export const NO_RENDER_PROCEED =
  "This is the normal state right after authoring: a render is captured only when a viewer opens the app (inline in chat or at its standalone page) — re-checking here does not trigger one, so do not poll. A clean validate_app static pass is enough to proceed; runtime diagnostics arrive on their own on the next render.";

// Authoring conventions for MCP Apps, kept in one place and embedded into the
// Build App built-in skill (built-in-skills.ts) so the per-tool descriptions can
// stay short. The CDN allowlist is derived from the policy that enforces it
// (APP_PLATFORM_CSP) so the two can never disagree.
/** @public — embedded into the Build App built-in skill (built-in-skills.ts). */
export const APP_AUTHORING_CONTRACT = `Author PURE UI HTML against the Archestra Apps SDK the platform injects at render time as window.archestra — never import an SDK, read __ARCHESTRA_APP_SDK_URL__, or wire postMessage yourself (that glue is provided; hand-rolling it breaks the app). window.archestra exists synchronously; await archestra.ready (a promise) before the first call. Every archestra method below is async — await it; archestra.user is the one plain property:
- archestra.user — the authenticated viewer ({id, name}), readable synchronously after ready, so no login flow is needed.
- archestra.storage.user.{get,set,list,delete} (all async) persist state private to each viewer (favorites, drafts, settings — the right default); archestra.storage.shared.* is one store all viewers share. Values are plain JSON (top-level null is not storable; use delete to clear a key). get(key) resolves to an entry {value, revision, owner} or null when absent; list() resolves to [{key, value, revision, owner}] (NOT an array of keys) in no guaranteed order — sort client-side. set(key, value) resolves to {revision, owner}. For multi-user writes use optimistic concurrency: pass set(key, value, {ifRevision}) with the revision you last read — a stale write rejects with {code: "conflict"} (re-read and retry). set(key, value, {owned: true}) claims a new shared key for the viewer so only they (or the app's author/admins) may overwrite it; an unauthorized write rejects with {code: "forbidden"}. Generate ids for new records with crypto.randomUUID(). Persist durable state through this attached store — never localStorage/sessionStorage/IndexedDB: the sandbox runs the app in an opaque origin where they throw, and even where they work they are ephemeral browser-local state, not the per-viewer/shared storage the platform provides here.
- archestra.tools.call(name, args) (async) calls an assigned tool as the viewing user with their credentials — pass name exactly as archestra.tools.list() returns it (the full assigned name, e.g. github__list_issues). It resolves with the tool's data directly: structuredContent when the tool provides it, else the JSON parsed from its text output, else the raw text string, else {media: [{type, mimeType, dataUrl}]} when the result is only images/audio (set dataUrl directly as an img/audio src), else null. It rejects with {code: "auth_required", url} when that tool's server still needs connecting — show err.message verbatim (it names the tool and carries the connect url; do NOT paraphrase it into a vague "connect the server" line) and render the url as a clickable link whose click handler calls archestra.ui.openLink(err.url), then let the user retry after connecting. A plain-text url or an <a target="_blank"> link is not enough: the sandbox blocks popups, so only archestra.ui.openLink (which asks the host to open it) works — in Archestra and in external MCP hosts alike. archestra.tools.list() returns the assigned tools.
- archestra.llm.complete(prompt, {system, jsonMode}) (async) runs ONE host LLM completion as the viewer (using the org's configured model — the app cannot choose one) and resolves to the model's text; use it to summarize, classify, extract, or generate over data the app ALREADY HAS (tool results, stored values, user input). jsonMode steers the model to return a single JSON value you then JSON.parse. It rejects with {code: "llm_quota"} when usage limits are reached and {code: "llm_unavailable"} otherwise. archestra.llm.prompt\`...\` is a tagged-template helper that builds a prompt string (pure, no call). This is NOT a data source — it cannot fetch anything; the tools-only rule below still governs all external data.
- archestra.ui.openLink(url) asks the host to open an external link in a new tab — the ONLY way to open one (the sandbox blocks popups, so <a target="_blank"> silently does nothing); archestra.ui.requestDisplayMode(mode) also reaches the host; archestra.context is the running app's {appId, version} (a plain property, readable after ready).
TOOLS-ONLY DATA RULE: ALL external data must come through assigned MCP tools — find one with search_tools, assign it via the tools param, call it with archestra.tools.call. The sandbox blocks network access (connect-src 'none'): fetch/XHR/WebSocket to any external API WILL FAIL and there is no per-app CSP override. The one exception is static assets (scripts, styles, fonts, images) from the platform CDN allowlist (${APP_PLATFORM_CSP_RESOURCE_DOMAINS.join(", ")}) — use it for client-side libraries, never as a data channel. A platform stylesheet is also injected: style against its theme variables (e.g. --color-text-primary/-secondary, --color-background-primary/-secondary, --color-border-primary, --color-accent, --border-radius-md, --font-sans/-mono; light/dark aware) and its .arch-* components (.arch-card, .arch-btn with --primary/--ghost, .arch-input, .arch-badge, .arch-spinner, .arch-tabs) — write only app-specific CSS, never a full theme, and never <link> the platform stylesheet yourself.`;

// Authoring-loop guidance, honest about preview_app_tool's human-approval gate
// and that render diagnostics only exist once a browser has rendered the app.
/** @public — embedded into the Build App built-in skill (built-in-skills.ts). */
export const APP_BUILD_LOOP_GUIDANCE = `Tool-calling apps follow a fixed order: assign the tool (search_tools to find it, then the tools param on scaffold_app for the initial set; replace that set at any time afterward with set_app_tools — edit_app and refine_app never touch assignments), then — interactively — call preview_app_tool with the app's id to observe the tool's real output shape before writing code that parses it (it needs human approval, and you cannot preview a tool that is not assigned yet — for a brand-new app, scaffold it first with a minimal scaffold; for one that already exists, assign the tool with set_app_tools first). If that approval is not granted, code defensively against the tool's documented schema instead. After an edit whose result was shown in chat, call get_app_diagnostics to read the diagnostics from the most recent render of the current version (a render happens when the app is shown inline in chat or at its standalone page — never because a tool was called). no_render_observed is the normal state when nobody is viewing the app: do not call get_app_diagnostics again to wait for a render — proceed on a clean validate_app static pass, and any runtime errors will instead arrive on the user's next message. To share an already-built app so others can run it — at any time, not only right after building — call publish_app to a team or the whole org (it changes the app's scope; team and org targets are role-gated).`;

// Condensed window.archestra surface carried inline in the scaffold_app (and
// refine_app) result so the authoring model has the SDK contract — and the real
// storage return shapes — in context before its first edit_app, without loading
// the full Build App skill (the gap that let a model emit the non-existent
// archestra.storage.get). Delivered once at scaffold time rather than repeated
// on every read_app, which just re-sends the same fixed block. The closing line
// is an escalation pointer: it names what the summary omits so the model knows
// when to load the full skill.
/** @public — surfaced in app tool results (apps.ts) and as capability.sdkSummary. */
export const ARCHESTRA_APP_SDK_SUMMARY = `window.archestra is injected at render time (await archestra.ready before the first call; every method below is async — await it). Surface:
- archestra.user — the authenticated viewer {id, name}, a plain property readable after ready.
- archestra.storage.user.{get,set,list,delete} — per-viewer JSON key/value store (archestra.storage.shared.* is one store all viewers share). get(key) resolves to {value, revision, owner} or null when absent (NOT the raw value); set(key, value) resolves to {revision, owner}; list() resolves to [{key, value, revision, owner}]. Never localStorage/sessionStorage (they throw in the sandbox's opaque origin).
- archestra.tools.call(name, args) — invoke an assigned MCP tool as the viewer with their credentials, name exactly as archestra.tools.list() returns it; resolves with the tool's data directly (structuredContent when provided, else JSON parsed from its text, else the raw text, else {media: [{type, mimeType, dataUrl}]} for image/audio-only results, else null); rejects with {code: "auth_required", url} when the tool's server still needs connecting — show err.message verbatim with the url as a clickable link that calls archestra.ui.openLink(err.url) (don't paraphrase the message; a plain link can't open — the sandbox blocks popups); archestra.tools.list() returns the assigned tools.
- archestra.llm.complete(prompt, {system, jsonMode}) — one host LLM completion over data the app already has (not a data source).
- archestra.ui.openLink(url) — the ONLY way to open an external link (the sandbox blocks popups, so <a target="_blank"> silently does nothing); archestra.ui.requestDisplayMode(mode) also reaches the host; archestra.context is the running app's {appId, version}.
All external data must come through assigned MCP tools (the sandbox blocks network access). This is the condensed SDK surface — for tool-calling apps (the assign→preview_app_tool→get_app_diagnostics build loop), the CSP/CDN allowlist, or theming against the platform stylesheet, load the Build App skill for the full contract.`;
