function appsNavLink(label, href, active) {
  const style = {
    background: "transparent", border: 0, padding: "6px 10px", borderRadius: 6,
    color: "var(--text-muted)", fontSize: 13, textDecoration: "none", cursor: "pointer",
  };
  return active
    ? <button className="active">{label}</button>
    : <a href={href} style={style}>{label}</a>;
}

function appStatus(app) {
  return app.manifest && Object.keys(app.manifest.tables || {}).length ? "published" : "draft";
}

function appDescription(app) {
  return app.manifest?.description || "A Supabase-backed AI-powered app with its own data and tools.";
}

function appPalette(app) {
  const palettes = ["#dce8e3", "#f9ead8", "#e0e3f1", "#e8e1f0"];
  const key = String(app.app_id || "").split("").reduce((sum, char) => sum + char.charCodeAt(0), 0);
  return palettes[key % palettes.length];
}

function appDataColumns(recordPage) {
  const manifestColumns = Array.isArray(recordPage?.fields) ? recordPage.fields.map((field) => field.name) : [];
  const items = Array.isArray(recordPage?.items) ? recordPage.items : [];
  const systemColumns = ["id", "created_at", "updated_at"].filter((name) => items.some((item) => Object.prototype.hasOwnProperty.call(item || {}, name)));
  return [...systemColumns, ...manifestColumns];
}

function appDataCell(value) {
  if (value === null || value === undefined) return { text: "—", title: "" };
  if (Array.isArray(value) || typeof value === "object") {
    try {
      const json = JSON.stringify(value);
      return { text: json, title: json };
    } catch (_) {}
  }
  const text = String(value);
  return { text, title: text };
}

function normalizePositiveNumber(value, fallback) {
  const parsed = Number(value);
  if (!Number.isFinite(parsed) || parsed < 1) return fallback;
  return Math.floor(parsed);
}

function normalizeAppRecordPage(recordPage, fallbackPage) {
  const items = Array.isArray(recordPage?.items) ? recordPage.items : [];
  const fields = Array.isArray(recordPage?.fields) ? recordPage.fields : [];
  const total = Math.max(0, Number(recordPage?.total || 0));
  const page_size = Math.min(100, Math.max(1, normalizePositiveNumber(recordPage?.page_size, 50)));
  const inferredTotalPages = total ? Math.ceil(total / page_size) : 0;
  const rawTotalPages = Math.max(0, Number(recordPage?.total_pages ?? inferredTotalPages) || 0);
  const total_pages = inferredTotalPages ? Math.max(inferredTotalPages, rawTotalPages) : rawTotalPages;
  const page = Math.min(total_pages || 1, normalizePositiveNumber(recordPage?.page, fallbackPage));

  return {
    ...recordPage,
    table: recordPage?.table || "",
    items,
    fields,
    total,
    page,
    page_size,
    total_pages,
  };
}

function appRecordRange(recordPage) {
  const total = Number(recordPage?.total || 0);
  if (!total) return "0 of 0 records";
  const items = Array.isArray(recordPage?.items) ? recordPage.items : [];
  const page = normalizePositiveNumber(recordPage?.page, 1);
  const page_size = Math.min(100, Math.max(1, normalizePositiveNumber(recordPage?.page_size, 50)));
  const start = Math.min(total, ((page - 1) * page_size) + 1);
  const end = items.length ? Math.min(total, start + items.length - 1) : start;
  return `${start}–${Math.max(start, end)} of ${total} records`;
}

function appPaginationPages(page, totalPages) {
  if (totalPages <= 1) return totalPages ? [1] : [];
  if (totalPages <= 7) return Array.from({ length: totalPages }, (_, index) => index + 1);
  const pages = [1];
  const start = Math.max(2, page - 1);
  const end = Math.min(totalPages - 1, page + 1);
  if (start > 2) pages.push("left-gap");
  for (let current = start; current <= end; current += 1) pages.push(current);
  if (end < totalPages - 1) pages.push("right-gap");
  pages.push(totalPages);
  return pages;
}

function readAppsUrlState() {
  const params = new URLSearchParams(window.location.search);
  return {
    appId: params.get("app_id"),
    view: params.get("view") || "settings",
  };
}

function AppDataPanel({ app, onBack }) {
  const [tables, setTables] = React.useState([]);
  const [tablesAppId, setTablesAppId] = React.useState(null);
  const [selectedTable, setSelectedTable] = React.useState("");
  const [page, setPage] = React.useState(1);
  const [searchInput, setSearchInput] = React.useState("");
  const [search, setSearch] = React.useState("");
  const [recordPage, setRecordPage] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const requestSeqRef = React.useRef(0);

  React.useEffect(() => {
    let cancelled = false;
    requestSeqRef.current += 1;
    setLoading(true);
    setError("");
    setTables([]);
    setTablesAppId(null);
    setSelectedTable("");
    setPage(1);
    setSearchInput("");
    setSearch("");
    setRecordPage(null);

    api.listAppTables(app.app_id)
      .then((items) => {
        if (cancelled) return;
        const nextTables = Array.isArray(items) ? items : [];
        setTables(nextTables);
        setTablesAppId(app.app_id);
      })
      .catch((err) => {
        if (cancelled) return;
        setError(err.message || "Unable to load App tables");
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });

    return () => { cancelled = true; };
  }, [app.app_id]);

  React.useEffect(() => {
    if (!tables.length) {
      setSelectedTable("");
      return;
    }
    if (!tables.some((table) => table.name === selectedTable)) {
      setSelectedTable(tables[0].name);
      setPage(1);
    }
  }, [tables, selectedTable]);

  React.useEffect(() => {
    const timeoutId = window.setTimeout(() => {
      const nextSearch = searchInput.trim();
      setSearch((previous) => previous === nextSearch ? previous : nextSearch);
      setPage(1);
    }, 250);
    return () => window.clearTimeout(timeoutId);
  }, [searchInput]);

  React.useEffect(() => {
    if (!selectedTable) {
      setRecordPage(null);
      return;
    }
    if (tablesAppId !== app.app_id) {
      setRecordPage(null);
      return;
    }
    if (!tables.some((table) => table.name === selectedTable)) {
      setRecordPage(null);
      return;
    }
    const requestSeq = requestSeqRef.current + 1;
    requestSeqRef.current = requestSeq;
    setRecordPage(null);
    setLoading(true);
    setError("");

    api.listAppRecordPage(app.app_id, selectedTable, { page, pageSize: 50, search })
      .then((nextPage) => {
        if (requestSeqRef.current !== requestSeq) return;
        setRecordPage(normalizeAppRecordPage(nextPage || {}, page));
      })
      .catch((err) => {
        if (requestSeqRef.current !== requestSeq) return;
        setRecordPage(null);
        setError(err.message || "Unable to load App records");
      })
      .finally(() => {
        if (requestSeqRef.current === requestSeq) setLoading(false);
      });
  }, [app.app_id, tables, tablesAppId, selectedTable, page, search]);

  const activeTable = tables.find((table) => table.name === selectedTable) || tables[0] || null;
  const columns = React.useMemo(() => appDataColumns(recordPage), [recordPage]);
  const pageButtons = React.useMemo(() => appPaginationPages(Number(recordPage?.page || page), Number(recordPage?.total_pages || 0)), [page, recordPage]);
  const hasTables = tables.length > 0;
  const items = Array.isArray(recordPage?.items) ? recordPage.items : [];
  const canGoPrevious = Number(recordPage?.page || page) > 1;
  const canGoNext = Number(recordPage?.page || page) < Number(recordPage?.total_pages || 0);

  return <div className="apps-data">
    <button className="apps-back" onClick={onBack}><Icon name="arrow-left" size={14} /> Back to Apps</button>
    <div className="apps-data-header">
      <div>
        <h1>{app.name} · Data</h1>
        <p>{app.app_id} · Browse published manifest tables and records.</p>
      </div>
      <span className={"apps-status " + appStatus(app)}>{appStatus(app) === "published" ? "Published" : "Draft"}</span>
    </div>
    <div className="apps-data-layout">
      <aside className="apps-data-rail">
        <div className="apps-data-rail-head">
          <h2>Tables</h2>
          <span>{tables.length}</span>
        </div>
        {!hasTables && !loading && !error && <div className="apps-data-state">No manifest tables published yet.</div>}
        {tables.map((table) => <button
          key={table.name}
          className={"apps-data-table-option" + (table.name === selectedTable ? " active" : "")}
          onClick={() => {
            setSelectedTable(table.name);
            setPage(1);
          }}
        >
          <span className="apps-data-table-name">{table.name}</span>
          <span className="apps-data-table-count">{table.record_count}</span>
        </button>)}
      </aside>
      <section className="apps-data-content">
        <div className="apps-data-content-head">
          <div>
            <h2>{activeTable ? activeTable.name : "Records"}</h2>
            <p>{activeTable ? `${activeTable.record_count} records · Read-only App data view.` : "Select a published table to browse records."}</p>
          </div>
          <label className="apps-search apps-data-search">
            <span className="search-icon"><Icon name="search" size={16} /></span>
            <input
              aria-label="Search records"
              placeholder={activeTable ? `Search ${activeTable.name}` : "Search records"}
              value={searchInput}
              disabled={!activeTable}
              onChange={(event) => setSearchInput(event.target.value)}
              onKeyDown={(event) => {
                if (event.key === "Enter") {
                  event.preventDefault();
                  setRecordPage(null);
                  setSearch(searchInput.trim());
                  setPage(1);
                }
              }}
            />
          </label>
        </div>

        {!hasTables && loading && <div className="apps-data-state">Loading tables…</div>}
        {!hasTables && error && <div className="apps-error">{error}</div>}
        {hasTables && error && <div className="apps-error">{error}</div>}
        {hasTables && !error && (
          <>
            <div className="apps-data-table-wrap">
              <table className="apps-data-table">
                <thead>
                  <tr>
                    {columns.map((name) => <th key={name}>{name}</th>)}
                  </tr>
                </thead>
                <tbody>
                  {loading && <tr><td className="apps-data-loading" colSpan={Math.max(columns.length, 1)}>Loading records…</td></tr>}
                  {!loading && items.length === 0 && <tr><td className="apps-data-empty-row" colSpan={Math.max(columns.length, 1)}>{search ? "No records match this search." : "No records yet for this table."}</td></tr>}
                  {!loading && items.map((item, rowIndex) => <tr key={item.id || `${selectedTable}-${rowIndex}`}>
                    {columns.map((name) => {
                      const cell = appDataCell(item?.[name]);
                      return <td key={name} title={cell.title || undefined}>{cell.text}</td>;
                    })}
                  </tr>)}
                </tbody>
              </table>
            </div>
            <div className="apps-data-footer">
              <span className="apps-data-summary">{recordPage ? appRecordRange(recordPage) : "0 of 0 records"}</span>
              <div className="apps-pagination">
                <button className="apps-page-button" disabled={loading || !canGoPrevious} onClick={() => setPage((current) => Math.max(1, current - 1))}>Previous</button>
                {pageButtons.map((item) => typeof item === "number"
                  ? <button
                    key={item}
                    className={"apps-page-button" + (item === Number(recordPage?.page || page) ? " active" : "")}
                    disabled={loading}
                    onClick={() => setPage(item)}
                  >
                    {item}
                  </button>
                  : <span key={item} className="apps-page-gap">…</span>)}
                <button className="apps-page-button" disabled={loading || !canGoNext} onClick={() => setPage((current) => current + 1)}>Next</button>
              </div>
            </div>
          </>
        )}
      </section>
    </div>
  </div>;
}

function AppSettingsPanel({ app, agents, onBack, onUpdated }) {
  const initialPolicy = app.manifest?.policy || {};
  const [manifestText, setManifestText] = React.useState(JSON.stringify(app.manifest || { app_id: app.app_id, tables: {} }, null, 2));
  const [authentication, setAuthentication] = React.useState(initialPolicy.authentication || "required");
  const [allowedOrigins, setAllowedOrigins] = React.useState(initialPolicy.allowed_origins || []);
  const [executor, setExecutor] = React.useState(app.executor_agent_id);
  const [busy, setBusy] = React.useState(false);
  const [message, setMessage] = React.useState("");
  const [error, setError] = React.useState("");
  const publish = async () => {
    setBusy(true); setError(""); setMessage("");
    try {
      const manifest = JSON.parse(manifestText);
      const normalizedOrigins = allowedOrigins.map((origin) => origin.trim());
      if (normalizedOrigins.some((origin) => !origin)) throw new Error("Each allowed origin must be a non-empty URL");
      const updated = await api.publishAppManifest(app.app_id, {
        ...manifest,
        app_id: app.app_id,
        policy: { ...manifest.policy, authentication, allowed_origins: normalizedOrigins },
      });
      onUpdated(updated); setMessage("Manifest published");
    } catch (err) { setError(err.message || "Unable to publish manifest"); }
    finally { setBusy(false); }
  };

  const replaceExecutor = async (value) => {
    setBusy(true); setError(""); setMessage("");
    try {
      const updated = await api.replaceAppExecutor(app.app_id, { executor_agent_id: value, expected_version: app.version });
      setExecutor(value); onUpdated(updated); setMessage("Executor updated");
    } catch (err) { setError(err.message || "Unable to update executor"); }
    finally { setBusy(false); }
  };

  return <div className="apps-settings">
    <button className="apps-back" onClick={onBack}><Icon name="arrow-left" size={14} /> Back to Apps</button>
    <div className="apps-settings-header"><div><h1>{app.name}</h1><p>{app.app_id} · App settings</p></div><span className={"apps-status " + appStatus(app)}>{appStatus(app) === "published" ? "Published" : "Draft"}</span></div>
    <div className="apps-settings-grid">
      <section className="apps-settings-card"><h2>App details</h2><div className="apps-settings-kv"><span>Owner type</span><strong>{app.owner_type}</strong><span>Owner ID</span><strong>{app.owner_id}</strong><span>Status</span><strong>{app.status}</strong><span>Version</span><strong>{app.version}</strong></div></section>
      <section className="apps-settings-card"><h2>Executor Agent</h2><p>Choose the Agent that can call and operate this App.</p><select className="apps-settings-select" value={executor} disabled={busy || !agents.length} onChange={(event) => replaceExecutor(event.target.value)}>{agents.length ? agents.map((agent) => <option key={agent.id} value={agent.id}>{agent.name} · {agent.id}</option>) : <option>No Agents available</option>}</select>{!agents.length && <a className="apps-field-help" href="/manage?new=agent">Create an Agent first</a>}</section>
    </div>
    <section className="apps-settings-card apps-policy-card">
      <div className="apps-settings-card-head"><div><h2>Runtime policy</h2><p>Control whether the App requires a platform login and which browser origins may call its Runtime API.</p></div></div>
      <div className="apps-policy-fields">
        <label className="apps-policy-field">Authentication
          <select name="authentication" className="apps-settings-select" value={authentication} disabled={busy} onChange={(event) => setAuthentication(event.target.value)}>
            <option value="required">Required — platform user Token is required</option>
            <option value="optional">Optional — anonymous sessions are allowed</option>
          </select>
        </label>
        <div className="apps-policy-field">
          <span className="apps-policy-label">Allowed origins</span>
          <p className="apps-field-help">Exact browser origins, for example <code>http://localhost:5173</code>. Do not include a path or <code>*</code>.</p>
          <div className="apps-origins-list">
            {allowedOrigins.map((origin, index) => <div className="apps-origin-row" key={`${index}-${origin}`}>
              <input name="allowed_origins" value={origin} disabled={busy} placeholder="https://your-app.example.com" onChange={(event) => setAllowedOrigins((current) => current.map((item, itemIndex) => itemIndex === index ? event.target.value : item))} />
              <button type="button" className="apps-origin-remove" disabled={busy} onClick={() => setAllowedOrigins((current) => current.filter((_, itemIndex) => itemIndex !== index))}>Remove origin</button>
            </div>)}
          </div>
          <button type="button" className="apps-origin-add" disabled={busy} onClick={() => setAllowedOrigins((current) => [...current, ""])}>+ Add origin</button>
        </div>
      </div>
    </section>
    <section className="apps-settings-card apps-manifest-card"><div className="apps-settings-card-head"><div><h2>Manifest</h2><p>Define the App schema and published data contract.</p></div><button className="btn primary sm" disabled={busy} onClick={publish}>{busy ? "Saving…" : "Publish manifest"}</button></div><textarea className="apps-manifest-editor" value={manifestText} onChange={(event) => setManifestText(event.target.value)} />{message && <div className="apps-settings-message">{message}</div>}{error && <div className="apps-form-error">{error}</div>}</section>
  </div>;
}

function AppPublishPanel({ app, onBack }) {
  const [releases, setReleases] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const [message, setMessage] = React.useState("");
  const [error, setError] = React.useState("");
  const inputRef = React.useRef(null);
  const load = async () => { try { setReleases(await api.listH5Releases(app.app_id)); } catch (err) { setError(err.message || "Unable to load H5 releases"); } };
  React.useEffect(() => { load(); }, [app.app_id]);
  const upload = async (event) => {
    const file = event.target.files?.[0]; event.target.value = ""; if (!file) return;
    setBusy(true); setError(""); setMessage("");
    try { const draft = await api.createH5Release(app.app_id); await api.uploadH5Release(app.app_id, draft.release_id, file); await load(); setMessage("H5 uploaded as draft. Publish it when ready."); } catch (err) { setError(err.message || "Unable to upload H5"); } finally { setBusy(false); }
  };
  const publish = async (releaseId) => { setBusy(true); setError(""); setMessage(""); try { await api.publishH5Release(app.app_id, releaseId); await load(); setMessage("H5 published to Aliyun OSS"); } catch (err) { setError(err.message || "Unable to publish H5"); } finally { setBusy(false); } };
  const copyUrl = async (url) => { try { await navigator.clipboard.writeText(url); setMessage("Aliyun OSS URL copied"); } catch (_) { setError("Unable to copy URL"); } };
  const rollback = async (releaseId) => { setBusy(true); setError(""); try { await api.rollbackH5Release(app.app_id, releaseId); await load(); setMessage("H5 release restored"); } catch (err) { setError(err.message || "Unable to rollback H5"); } finally { setBusy(false); } };
  const current = releases.find((release) => release.current);
  return <div className="apps-settings">
    <button className="apps-back" onClick={onBack}><Icon name="arrow-left" size={14} /> Back to Apps</button>
    <div className="apps-settings-header"><div><h1>{app.name} · Publish</h1><p>{app.app_id} · Upload and publish your H5 app to Aliyun OSS.</p></div><span className={"apps-status " + appStatus(app)}>{appStatus(app) === "published" ? "Published" : "Draft"}</span></div>
    <section className="apps-settings-card apps-h5-card"><div className="apps-settings-card-head"><div><h2>H5 Hosting</h2><p>Upload a static H5 ZIP for validation, then publish a selected version to Aliyun OSS.</p></div><button className="btn primary sm" disabled={busy} onClick={() => inputRef.current?.click()}>{busy ? "Working…" : "Upload H5"}</button><input ref={inputRef} type="file" accept=".zip,application/zip" hidden onChange={upload} /></div>
      <div className="apps-h5-current-row"><span>Current version</span><strong>{current ? `Version ${current.version}` : "Not published"}</strong>{current?.public_url && <><a href={current.public_url} target="_blank" rel="noreferrer">Open H5</a><button className="apps-origin-add" onClick={() => copyUrl(current.public_url)}>Copy URL</button></>}</div>
      <div className="apps-h5-releases">{releases.length ? releases.map((release) => <div className="apps-h5-release" key={release.release_id}><div><strong>Version {release.version}</strong><span className={"apps-h5-status " + release.status}>{release.status}</span><p>{release.file_count} files · Manifest v{release.manifest_version}{release.checksum ? ` · ${release.checksum.slice(0, 12)}` : ""}</p>{release.public_url && <div className="apps-h5-url"><span>Aliyun OSS URL</span><a href={release.public_url} target="_blank" rel="noreferrer">{release.public_url}</a><button className="apps-origin-add" onClick={() => copyUrl(release.public_url)}>Copy URL</button></div>}</div><div className="apps-h5-release-actions">{release.current ? <span className="apps-h5-current">Current</span> : release.status === "draft" ? <><a className="apps-origin-add" href={api.previewH5Release(app.app_id, release.release_id, release.entry_file, release.preview_url)} target="_blank" rel="noreferrer">Preview</a><button className="btn primary sm" disabled={busy} onClick={() => publish(release.release_id)}>Publish to Aliyun OSS</button></> : release.status === "published" && <button className="apps-origin-remove" disabled={busy} onClick={() => rollback(release.release_id)}>Rollback</button>}</div></div>) : <p className="apps-field-help">No H5 release yet. Upload a ZIP containing index.html to create the first draft.</p>}</div>
    </section>
    {message && <div className="apps-settings-message">{message}</div>}{error && <div className="apps-form-error">{error}</div>}
  </div>;
}

function AppsPage() {
  const [apps, setApps] = React.useState([]);
  const [agents, setAgents] = React.useState([]);
  const [filter, setFilter] = React.useState("all");
  const [query, setQuery] = React.useState("");
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const [createOpen, setCreateOpen] = React.useState(false);
  const [createBusy, setCreateBusy] = React.useState(false);
  const [createError, setCreateError] = React.useState("");
  const [form, setForm] = React.useState({ app_id: "", name: "", owner_type: "user", owner_id: api.userId || "", executor_agent_id: "" });
  const [urlState, setUrlState] = React.useState(readAppsUrlState);

  React.useEffect(() => {
    const syncUrlState = () => {
      setUrlState(readAppsUrlState());
    };
    window.addEventListener("popstate", syncUrlState);
    return () => {
      window.removeEventListener("popstate", syncUrlState);
    };
  }, []);

  React.useEffect(() => {
    Promise.all([api.listApps(), api.listAgents().catch(() => [])])
      .then(([items, agentItems]) => {
        setApps(Array.isArray(items) ? items : []);
        const safeAgents = Array.isArray(agentItems) ? agentItems : [];
        setAgents(safeAgents);
        setForm((previous) => ({ ...previous, executor_agent_id: previous.executor_agent_id || safeAgents[0]?.id || "" }));
      })
      .catch((err) => setError(err.message || "Unable to load Apps"))
      .finally(() => setLoading(false));
  }, []);

  const setField = (key, value) => setForm((previous) => ({ ...previous, [key]: value }));
  const changeOwnerType = (owner_type) => {
    setForm((previous) => ({
      ...previous,
      owner_type,
      owner_id: owner_type === "user" ? (api.userId || "") : (agents[0]?.id || ""),
    }));
  };
  const createApp = async (event) => {
    event.preventDefault();
    setCreateBusy(true);
    setCreateError("");
    try {
      const created = await api.createApp(form);
      setApps((previous) => [created, ...previous]);
      setCreateOpen(false);
      setForm({ app_id: "", name: "", owner_type: "user", owner_id: api.userId || "", executor_agent_id: agents[0]?.id || "" });
    } catch (err) {
      setCreateError(err.message || "Unable to create App");
    } finally {
      setCreateBusy(false);
    }
  };

  const filtered = apps.filter((app) => {
    const status = appStatus(app);
    const matchesFilter = filter === "all" || (filter === "published" && status === "published") || (filter === "drafts" && status === "draft");
    const needle = query.trim().toLowerCase();
    const matchesQuery = !needle || [app.name, app.app_id, appDescription(app)].some((value) => String(value || "").toLowerCase().includes(needle));
    return matchesFilter && matchesQuery;
  });
  const selectedAppId = urlState.appId;
  const selectedApp = apps.find((app) => app.app_id === selectedAppId);
  const selectedView = urlState.view;

  const backToApps = () => {
    window.history.pushState({}, "", "/apps");
    setUrlState(readAppsUrlState());
  };

  return (
    <div className="app apps-page">
      <div className="topbar">
        <div className="brand"><div className="dot">百</div>baizhi</div>
        <div className="tabs">
          {appsNavLink("Playground", "/playground", false)}
          {appsNavLink("Manage", "/manage", false)}
          {appsNavLink("Evals", "/evals", false)}
          {appsNavLink("Traces", "/traces", false)}
          {appsNavLink("Marketplace", "/marketplace", false)}
          {appsNavLink("Apps", "/apps", true)}
          {appsNavLink("API Docs", "/api-docs", false)}
        </div>
        <div className="spacer" />
        <div className="actions"><span className="user-pref" id="apps-user-label" /></div>
      </div>
      <main className="apps-main">
        {selectedApp ? (selectedView === "data"
          ? <AppDataPanel app={selectedApp} onBack={backToApps} />
          : selectedView === "publish"
            ? <AppPublishPanel app={selectedApp} onBack={backToApps} />
            : <AppSettingsPanel app={selectedApp} agents={agents} onBack={backToApps} onUpdated={(updated) => setApps((previous) => previous.map((item) => item.app_id === updated.app_id ? updated : item))} />
        ) : <>
        <header className="apps-header">
          <div><h1>Apps</h1><p>Build, manage and publish your AI-powered apps.</p></div>
          <button className="btn primary apps-create" onClick={() => { setCreateError(""); setCreateOpen(true); }}><Icon name="plus" size={13} /> Create app</button>
        </header>
        <div className="apps-toolbar">
          <div className="apps-filters">
            {[['all', 'All apps'], ['published', 'Published'], ['drafts', 'Drafts']].map(([value, label]) => (
              <button key={value} className={"apps-filter" + (filter === value ? " active" : "")} onClick={() => setFilter(value)}>{label}</button>
            ))}
          </div>
          <label className="apps-search"><span className="search-icon"><Icon name="search" size={16} /></span><input aria-label="Search apps" placeholder="Search apps" value={query} onChange={(event) => setQuery(event.target.value)} /></label>
        </div>
        {loading && <div className="apps-list">{[1, 2].map((item) => <div className="apps-card" key={item} aria-label="Loading app"><div className="apps-preview" /><div className="apps-card-content"><div className="apps-card-title"><h2>Loading…</h2></div></div></div>)}</div>}
        {!loading && error && <div className="apps-error">{error}</div>}
        {!loading && !error && filtered.length === 0 && <div className="apps-empty"><h2>{apps.length ? "No matching apps" : "No apps yet"}</h2><p>{apps.length ? "Try another filter or search term." : "Create your first AI-powered app to get started."}</p>{!apps.length && <button className="btn primary" onClick={() => setCreateOpen(true)}><Icon name="plus" size={13} /> Create app</button>}</div>}
        {!loading && !error && filtered.length > 0 && <div className="apps-list">{filtered.map((app) => {
          const status = appStatus(app);
          return <article className="apps-card" key={app.app_id}>
            <div className="apps-preview" style={{ "--preview-bg": appPalette(app) }}><div className="apps-preview-bar"><span /><span /><span /></div><div className="apps-preview-line" /><div className="apps-preview-line long" /></div>
            <div className="apps-card-content">
              <div className="apps-card-title"><h2>{app.name}</h2><span className={"apps-status " + status}>{status === "published" ? "Published" : "Draft"}</span></div>
              <p className="apps-card-description">{appDescription(app)}</p>
              <div className="apps-card-actions">
                <a className="apps-card-action" href={`/apps?app_id=${encodeURIComponent(app.app_id)}&view=settings`}><Icon name="settings" size={14} /> Setting</a>
                <a className="apps-card-action" href={`/apps?app_id=${encodeURIComponent(app.app_id)}&view=data`}><Icon name="database" size={14} /> Data</a>
                <a className="apps-card-action" href={`/apps?app_id=${encodeURIComponent(app.app_id)}&view=preview`}><Icon name="external" size={14} /> Preview</a>
                <a className="apps-card-action publish" href={`/apps?app_id=${encodeURIComponent(app.app_id)}&view=publish`}><Icon name="upload" size={14} /> Publish</a>
              </div>
            </div>
            <button className="apps-card-menu" aria-label={`More actions for ${app.name}`}>···</button>
            <span className="apps-card-updated">Updated recently</span>
          </article>;
        })}</div>}
        </>}
      </main>
      {createOpen && <div className="apps-modal-backdrop" role="presentation" onMouseDown={(event) => { if (event.target === event.currentTarget && !createBusy) setCreateOpen(false); }}>
        <form className="apps-modal" onSubmit={createApp}>
          <div className="apps-modal-head"><div><h2>Create app</h2><p>Create a Supabase-backed App space.</p></div><button type="button" className="apps-modal-close" aria-label="Close" disabled={createBusy} onClick={() => setCreateOpen(false)}>×</button></div>
          <div className="apps-form-grid">
            <label>App ID<input required value={form.app_id} placeholder="app001" onChange={(event) => setField("app_id", event.target.value)} /></label>
            <label>Name<input required value={form.name} placeholder="Tasks" onChange={(event) => setField("name", event.target.value)} /></label>
            <label>Owner type<select value={form.owner_type} onChange={(event) => changeOwnerType(event.target.value)}><option value="user">User</option><option value="agent">Agent</option></select></label>
            <label>Owner ID{form.owner_type === "user" ? <input required readOnly value={api.userId || ""} title="The current logged-in user owns this App" /> : <select required value={form.owner_id} onChange={(event) => setField("owner_id", event.target.value)}><option value="" disabled>{agents.length ? "Select an Agent" : "No Agents available"}</option>{agents.map((agent) => <option key={agent.id} value={agent.id}>{agent.name} · {agent.id}</option>)}</select>}{form.owner_type === "agent" && !agents.length && <a className="apps-field-help" href="/manage?new=agent">Create an Agent first</a>}</label>
            <label className="full">Executor Agent<select required value={form.executor_agent_id} onChange={(event) => setField("executor_agent_id", event.target.value)}><option value="" disabled>{agents.length ? "Select an Agent" : "No Agents available"}</option>{agents.map((agent) => <option key={agent.id} value={agent.id}>{agent.name} · {agent.id}</option>)}</select>{!agents.length && <a className="apps-field-help" href="/manage?new=agent">Create an Agent first</a>}</label>
          </div>
          {createError && <div className="apps-form-error">{createError}</div>}
          <div className="apps-modal-actions"><button type="button" className="btn" disabled={createBusy} onClick={() => setCreateOpen(false)}>Cancel</button><button type="submit" className="btn primary" disabled={createBusy || !agents.length}>{createBusy ? "Creating…" : "Create app"}</button></div>
        </form>
      </div>}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<AppsPage />);
