// IndieVertical - admin panel: overview, page moderation + curation, user
// management, newsletter, message moderation, audit log.
// The UI is gated by state.isAdmin (cosmetic); real enforcement is that every
// query/mutation goes through an admin-gated SECURITY DEFINER RPC.

const IV_ADMIN_FILTERS = [
  { key: "published", label: "Published" },
  { key: "rejected", label: "Taken down" },
  { key: "draft", label: "Drafts" },
  { key: "", label: "All" },
];

// ---------- overview ----------
function IvAdminOverview() {
  const [stats, setStats] = React.useState(null);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    SUPA.rpc("admin_overview_stats").then(({ data }) => { setStats(data || {}); setLoading(false); });
  }, []);

  const card = (label, value) => (
    <div className="sc"><div className="v">{(value || 0).toLocaleString()}</div><div className="l">{label}</div></div>
  );

  return (
    <React.Fragment>
      <div className="main-h"><h1>Overview</h1></div>
      {loading ? <div className="loading-inline">Loading&hellip;</div> : (
        <React.Fragment>
          <h2 className="h2-small">Users</h2>
          <div className="statcards">
            {card("Total users", stats.users_total)}
            {card("Developers", stats.devs)}
            {card("Scouts", stats.scouts)}
            {card("Suspended", stats.suspended)}
          </div>
          <h2 className="h2-small">Pages</h2>
          <div className="statcards">
            {card("Published", stats.projects_published)}
            {card("Drafts", stats.projects_draft)}
            {card("Taken down", stats.projects_rejected)}
            {card("Total pages", stats.projects_total)}
          </div>
          <h2 className="h2-small">Engagement</h2>
          <div className="statcards">
            {card("Page views", stats.total_views)}
            {card("Shortlist saves", stats.total_saves)}
            {card("Subscribers", stats.subscribers)}
            {card("New signups (7d)", stats.signups_7d)}
          </div>
        </React.Fragment>
      )}
    </React.Fragment>
  );
}

// ---------- pages: moderation + curation ----------
function IvAdminPages() {
  const { api } = useStore();
  const [items, setItems] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [filter, setFilter] = React.useState("published");
  const [busy, setBusy] = React.useState(false);

  const load = React.useCallback(() => {
    setLoading(true);
    SUPA.rpc("admin_list_projects", { p_status: filter || null }).then(({ data, error }) => {
      setItems(error ? [] : (data || []));
      setLoading(false);
    });
  }, [filter]);

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

  const act = async (rpc, params, okMsg) => {
    setBusy(true);
    const { error } = await SUPA.rpc(rpc, params);
    setBusy(false);
    if (error) { api.showToast(error.message || "Action failed. Please try again."); return; }
    if (okMsg) api.showToast(okMsg);
    load();
  };

  const takeDown = async (slug) => {
    const note = window.prompt("Reason for taking this page down (shown to the developer):", "");
    if (note === null) return;
    setBusy(true);
    const { error } = await SUPA.rpc("admin_review_project", { p_slug: slug, p_decision: "taken_down", p_note: note });
    if (!error) {
      // Best-effort takedown email; the takedown itself is already recorded.
      SUPA.functions.invoke("page-takedown-email", { body: { slug, note } }).catch(() => {});
    }
    setBusy(false);
    if (error) { api.showToast(error.message || "Action failed. Please try again."); return; }
    api.showToast("Page taken down.");
    load();
  };
  const reinstate = (slug) =>
    act("admin_review_project", { p_slug: slug, p_decision: "reinstated", p_note: null }, "Page reinstated.");
  const togglePick = (p) =>
    act("admin_set_editors_pick", { p_slug: p.slug, p_value: !p.editors_pick },
      !p.editors_pick ? "Added to Editor's Picks." : "Removed from Editor's Picks.");
  const togglePromoted = (p) => {
    if (p.promoted) {
      act("admin_set_promoted", { p_slug: p.slug, p_value: false, p_until: null }, "Promotion removed.");
      return;
    }
    const ans = window.prompt("Promote for how many days? (leave blank for no expiry)", "30");
    if (ans === null) return;
    const days = parseInt(ans, 10);
    const until = ans.trim() && days > 0 ? new Date(Date.now() + days * 86400000).toISOString() : null;
    act("admin_set_promoted", { p_slug: p.slug, p_value: true, p_until: until }, "Marked as Promoted.");
  };

  return (
    <React.Fragment>
      <div className="main-h"><h1>Pages</h1></div>
      <div className="adm-filters">
        {IV_ADMIN_FILTERS.map((f) => (
          <button key={f.key} type="button"
            className={"ftoggle" + (filter === f.key ? " on" : "")}
            onClick={() => setFilter(f.key)}>{f.label}</button>
        ))}
      </div>

      {loading ? (
        <div className="loading-inline">Loading&hellip;</div>
      ) : items.length === 0 ? (
        <IvEmptyState heading="Nothing here" body="No pages match this filter." />
      ) : (
        <div className="adm-list">
          {items.map((p) => (
            <div className="prow" key={p.slug}>
              <div className={"p-thumb " + (p.art || "")}
                style={p.key_art_url ? { backgroundImage: "url(" + p.key_art_url + ")", backgroundSize: "cover", backgroundPosition: "center" } : undefined}></div>
              <div>
                <div className="p-t">{p.title || "Untitled"} <span className={"pstatus" + (p.status === "rejected" ? " draft" : "")}>{p.status}</span></div>
                <div className="p-meta">
                  {(p.stats_views || 0).toLocaleString()} views · {p.stats_saves || 0} saves · {p.dev_status} · {p.deal_type}
                </div>
                <div className="adm-flags">
                  <button type="button" className={"chiptog" + (p.editors_pick ? " on" : "")} disabled={busy} onClick={() => togglePick(p)}>★ Editor's Pick</button>
                  <button type="button" className={"chiptog" + (p.promoted ? " on" : "")} disabled={busy} onClick={() => togglePromoted(p)}>
                    Promoted{p.promoted && p.promoted_until ? " · until " + new Date(p.promoted_until).toLocaleDateString() : ""}
                  </button>
                </div>
                {p.status === "rejected" && p.review_note && (
                  <div className="p-meta" style={{ color: "var(--warm)" }}>Takedown note: {p.review_note}</div>
                )}
              </div>
              <div className="p-actions">
                <a className="btn btn-secondary" href={"/game/" + p.slug}>View</a>
                {p.status === "rejected"
                  ? <button type="button" className="btn btn-secondary" disabled={busy} onClick={() => reinstate(p.slug)}>Reinstate</button>
                  : <button type="button" className="btn btn-secondary" disabled={busy} onClick={() => takeDown(p.slug)}>Take down</button>}
              </div>
            </div>
          ))}
        </div>
      )}
    </React.Fragment>
  );
}

// ---------- users ----------
function IvAdminUsers() {
  const { api } = useStore();
  const [users, setUsers] = React.useState([]);
  const [adminSet, setAdminSet] = React.useState(new Set());
  const [loading, setLoading] = React.useState(true);
  const [query, setQuery] = React.useState("");
  const [busy, setBusy] = React.useState(false);

  const load = React.useCallback(() => {
    setLoading(true);
    Promise.all([
      SUPA.rpc("admin_list_users", { p_query: query || null }),
      SUPA.rpc("admin_list_emails"),
    ]).then(([u, a]) => {
      setUsers(u.error ? [] : (u.data || []));
      setAdminSet(new Set((a.data || []).map((r) => (r.email || "").toLowerCase())));
      setLoading(false);
    });
  }, [query]);

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

  const act = async (rpc, params, okMsg) => {
    setBusy(true);
    const { error } = await SUPA.rpc(rpc, params);
    setBusy(false);
    if (error) { api.showToast(error.message || "Action failed. Please try again."); return; }
    if (okMsg) api.showToast(okMsg);
    load();
  };

  const setRole = (u, role) => {
    if (role === u.role) return;
    act("admin_update_user", { p_id: u.id, p_patch: { role } }, "Role updated.");
  };
  const editName = (u) => {
    const name = window.prompt("Display name:", u.display_name || "");
    if (name === null) return;
    act("admin_update_user", { p_id: u.id, p_patch: { display_name: name } }, "Profile updated.");
  };
  const toggleSuspend = (u) =>
    act("admin_set_user_suspended", { p_id: u.id, p_value: !u.suspended },
      u.suspended ? "User restored." : "User suspended.");
  const toggleAdmin = (u) => {
    if (!u.email) { api.showToast("This user has no email on record."); return; }
    const isAdmin = adminSet.has(u.email.toLowerCase());
    act(isAdmin ? "admin_remove_email" : "admin_add_email", { p_email: u.email },
      isAdmin ? "Admin access removed." : "Granted admin access.");
  };
  const setApproved = async (u, value) => {
    setBusy(true);
    const { error } = await SUPA.rpc("admin_set_scout_approved", { p_id: u.id, p_value: value });
    if (!error && value) {
      // Best-effort approval email; approval + inbox notification already done.
      SUPA.functions.invoke("publisher-approved-email", { body: { user_id: u.id } }).catch(() => {});
    }
    setBusy(false);
    if (error) { api.showToast(error.message || "Action failed. Please try again."); return; }
    api.showToast(value ? "Publisher approved." : "Approval revoked.");
    load();
  };

  return (
    <React.Fragment>
      <div className="main-h"><h1>Users</h1></div>
      <div className="adm-filters">
        <input className="f-input" style={{ maxWidth: 320 }} placeholder="Search name, email, studio…"
          value={query} onChange={(e) => setQuery(e.target.value)} />
      </div>
      {loading ? <div className="loading-inline">Loading&hellip;</div>
        : users.length === 0 ? <IvEmptyState heading="No users" body="No users match this search." />
        : (
          <div className="adm-list">
            {users.map((u) => {
              const isAdmin = u.email && adminSet.has(u.email.toLowerCase());
              return (
                <div className="prow adm-user" key={u.id}>
                  <div>
                    <div className="p-t">
                      {u.display_name || "(no name)"}
                      {isAdmin && <span className="pstatus" style={{ borderColor: "var(--cool)", color: "var(--cool)" }}>Admin</span>}
                      {u.role === "scout" && !u.scout_approved && <span className="pstatus" style={{ borderColor: "var(--warm)", color: "var(--warm)" }}>Publisher pending</span>}
                      {u.role === "scout" && u.scout_approved && <span className="pstatus">Publisher</span>}
                      {u.suspended && <span className="pstatus" style={{ borderColor: "var(--warm)", color: "var(--warm)" }}>Suspended</span>}
                    </div>
                    <div className="p-meta">{u.email || "no email"} · {u.studio_name || u.org_name || "no org"}</div>
                    <div className="adm-flags">
                      <select className="f-input adm-role" value={u.role} disabled={busy} onChange={(e) => setRole(u, e.target.value)}>
                        <option value="dev">dev</option>
                        <option value="scout">scout</option>
                      </select>
                      <button type="button" className="chiptog" disabled={busy} onClick={() => editName(u)}>Edit name</button>
                      <button type="button" className={"chiptog" + (isAdmin ? " on" : "")} disabled={busy} onClick={() => toggleAdmin(u)}>Admin</button>
                    </div>
                  </div>
                  <div className="p-actions">
                    {u.role === "scout" && (
                      u.scout_approved
                        ? <button type="button" className="btn btn-secondary" disabled={busy} onClick={() => setApproved(u, false)}>Revoke publisher</button>
                        : <button type="button" className="btn btn-primary" disabled={busy} onClick={() => setApproved(u, true)}>Approve publisher</button>
                    )}
                    <button type="button" className="btn btn-secondary" disabled={busy} onClick={() => toggleSuspend(u)}>
                      {u.suspended ? "Restore" : "Suspend"}
                    </button>
                  </div>
                </div>
              );
            })}
          </div>
        )}
    </React.Fragment>
  );
}

// ---------- newsletter ----------
function IvAdminSubscribers() {
  const [rows, setRows] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    SUPA.rpc("admin_list_subscribers", { p_segment: null }).then(({ data, error }) => {
      setRows(error ? [] : (data || []));
      setLoading(false);
    });
  }, []);

  const exportCsv = () => {
    const header = ["email", "segment", "source", "consent_at", "unsubscribed"];
    const lines = [header.join(",")].concat(rows.map((r) =>
      [r.email, r.segment, r.source, r.consent_at, r.unsubscribed].map((v) =>
        '"' + String(v == null ? "" : v).replace(/"/g, '""') + '"').join(",")));
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = "subscribers.csv"; a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <React.Fragment>
      <div className="main-h">
        <h1>Newsletter</h1>
        {rows.length > 0 && <button type="button" className="btn btn-primary" onClick={exportCsv}>Export CSV</button>}
      </div>
      {loading ? <div className="loading-inline">Loading&hellip;</div>
        : rows.length === 0 ? <IvEmptyState heading="No subscribers yet" body="Newsletter signups will show up here." />
        : (
          <div className="adm-audit">
            {rows.map((r) => (
              <div className="adm-audit-row" key={r.id}>
                <div className="aa-action">{r.email}{r.unsubscribed && <span className="aa-target"> · unsubscribed</span>}</div>
                <div className="aa-meta">{r.segment} · {r.source || "no source"} · {new Date(r.created_at).toLocaleDateString()}</div>
              </div>
            ))}
          </div>
        )}
    </React.Fragment>
  );
}

// ---------- message moderation (read-only) ----------
function IvAdminThreads() {
  const [threads, setThreads] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [open, setOpen] = React.useState(null);     // thread being viewed
  const [messages, setMessages] = React.useState([]);
  const [mLoading, setMLoading] = React.useState(false);

  React.useEffect(() => {
    SUPA.rpc("admin_list_threads").then(({ data, error }) => {
      setThreads(error ? [] : (data || []));
      setLoading(false);
    });
  }, []);

  const view = (t) => {
    setOpen(t); setMLoading(true); setMessages([]);
    SUPA.rpc("admin_get_thread", { p_thread_id: t.id }).then(({ data, error }) => {
      setMessages(error ? [] : (data || []));
      setMLoading(false);
    });
  };

  return (
    <React.Fragment>
      <div className="main-h"><h1>Messages</h1></div>
      {loading ? <div className="loading-inline">Loading&hellip;</div>
        : threads.length === 0 ? <IvEmptyState heading="No conversations" body="Dev/scout message threads will appear here." />
        : open ? (
          <React.Fragment>
            <button type="button" className="btn btn-secondary" style={{ marginBottom: 16 }} onClick={() => setOpen(null)}>← Back to threads</button>
            <h2 className="h2-small">{open.game_name || open.game_slug}</h2>
            {mLoading ? <div className="loading-inline">Loading&hellip;</div> : (
              <div className="adm-thread">
                {messages.map((m) => (
                  <div key={m.id} className={"adm-msg adm-msg-" + m.from_side}>
                    <div className="aa-meta">{m.from_side} · {new Date(m.created_at).toLocaleString()}</div>
                    <div>{m.text}</div>
                  </div>
                ))}
              </div>
            )}
          </React.Fragment>
        ) : (
          <div className="adm-audit">
            {threads.map((t) => (
              <div className="adm-audit-row adm-thread-row" key={t.id} onClick={() => view(t)}>
                <div className="aa-action">{t.game_name || t.game_slug}</div>
                <div className="aa-meta">
                  {t.last_message_text || "No messages"} · {t.last_message_at ? new Date(t.last_message_at).toLocaleString() : ""}
                </div>
              </div>
            ))}
          </div>
        )}
    </React.Fragment>
  );
}

// ---------- audit log ----------
function IvAdminAudit() {
  const [rows, setRows] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    SUPA.rpc("admin_list_audit", { p_limit: 200 }).then(({ data, error }) => {
      setRows(error ? [] : (data || []));
      setLoading(false);
    });
  }, []);

  return (
    <React.Fragment>
      <div className="main-h"><h1>Audit log</h1></div>
      {loading ? (
        <div className="loading-inline">Loading&hellip;</div>
      ) : rows.length === 0 ? (
        <IvEmptyState heading="No admin actions yet" body="Approvals, takedowns, and curation changes will be logged here." />
      ) : (
        <div className="adm-audit">
          {rows.map((r) => (
            <div className="adm-audit-row" key={r.id}>
              <div className="aa-action">{r.action}{r.target ? <span className="aa-target"> · {r.target}</span> : null}</div>
              <div className="aa-meta">{r.admin_email || r.admin_id} · {new Date(r.created_at).toLocaleString()}</div>
              {r.details && Object.keys(r.details).length > 0 && (
                <div className="aa-details">{JSON.stringify(r.details)}</div>
              )}
            </div>
          ))}
        </div>
      )}
    </React.Fragment>
  );
}

// ---------- shell ----------
const IV_ADMIN_TABS = {
  "": IvAdminOverview,
  pages: IvAdminPages,
  users: IvAdminUsers,
  newsletter: IvAdminSubscribers,
  messages: IvAdminThreads,
  audit: IvAdminAudit,
};

function IvAdminPanel({ parts }) {
  const tab = (parts && parts[1]) || "";
  const Screen = IV_ADMIN_TABS[tab] || IvAdminOverview;
  return (
    <div className="pp dir-marquee">
      <IvHeader />
      <div className="app" style={{ flex: 1 }}>
        <IvSidebar items={[
          { label: "Overview", href: "/admin", on: tab === "" },
          { label: "Pages", href: "/admin/pages", on: tab === "pages" },
          { label: "Users", href: "/admin/users", on: tab === "users" },
          { label: "Newsletter", href: "/admin/newsletter", on: tab === "newsletter" },
          { label: "Messages", href: "/admin/messages", on: tab === "messages" },
          { label: "Audit log", href: "/admin/audit", on: tab === "audit" },
        ]} />
        <div className="main">
          <Screen />
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  IvAdminPanel, IvAdminOverview, IvAdminPages, IvAdminUsers,
  IvAdminSubscribers, IvAdminThreads, IvAdminAudit,
});
