// App root — router + state

const ClearCastContext = React.createContext(null);

function useClearCast() {
  return React.useContext(ClearCastContext);
}

function PublicBioPage({ handle }) {
  const hub = useClearCast();
  const page = hub.data.linkBioPage || {};
  const links = (hub.data.linkBio || [])
    .filter((link) => link.active !== false)
    .sort((a, b) => (a.order || 0) - (b.order || 0));
  const theme = page.theme || "warm";
  const bg = theme === "dark" ? "linear-gradient(180deg, #1a1a1a, #0a0a0a)" : theme === "rose" ? "linear-gradient(180deg, #ffd8d0, #d88880)" : theme === "forest" ? "linear-gradient(180deg, #284038, #142420)" : "linear-gradient(180deg, #f4e8d0, #d8b890)";
  const light = theme === "warm" || theme === "rose";
  const click = async (link) => {
    await hub.actions.clickBioLink(link.id);
    if (link.url?.startsWith("http")) window.open(link.url, "_blank");
  };

  if (handle && page.handle && handle !== page.handle) {
    return <div className="public-bio" style={{ background: bg }}><div className="empty-note">This bio page was not found.</div></div>;
  }

  return (
    <div className="public-bio" style={{ background: bg, color: light ? "#2a1a0a" : "white" }}>
      <div className="public-bio-card">
        <div className="brand-mark" style={{ width: 72, height: 72, borderRadius: 999, fontSize: 22, margin: "0 auto 14px" }}>CC</div>
        <h1>@{page.handle || "emma"}</h1>
        <p>{page.bio}</p>
        <div className="public-bio-links">
          {links.map((link) => (
            <button key={link.id} onClick={() => click(link)} className="public-bio-link">
              <span>{link.icon || "✦"}</span>
              <b>{link.title}</b>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

function OnboardingPage() {
  const hub = useClearCast();
  const data = hub.data || {};
  const setPage = hub.setPage;
  const businessName = data.workspace?.name || "your business";
  const connected = (data.integrations || []).filter((integration) => integration.connected).length;
  const postCount = (data.posts || []).length;
  const scheduled = (data.posts || []).filter((post) => ["scheduled", "published"].includes(post.status)).length;
  const steps = [
    { key: "connect", title: "Connect your accounts", desc: "Link the social channels you post to — you can add more anytime.", done: connected > 0, cta: connected > 0 ? "Manage channels" : "Connect channels", go: "integrations" },
    { key: "create", title: "Create your first post", desc: "Write it yourself or generate a caption, image, or video with AI.", done: postCount > 0, cta: "Create a post", go: "create" },
    { key: "schedule", title: "Schedule it", desc: "Pick the best time and add it to your calendar.", done: scheduled > 0, cta: "Open calendar", go: "calendar" }
  ];
  const doneCount = steps.filter((step) => step.done).length;
  const skip = () => {
    try {
      const map = JSON.parse(window.localStorage.getItem("clearcast-onboard-skip") || "{}");
      map[data.activeWorkspaceId] = true;
      window.localStorage.setItem("clearcast-onboard-skip", JSON.stringify(map));
    } catch (error) { /* ignore */ }
    setPage("create");
  };
  return (
    <div className="page">
      <div className="onboarding-wrap">
        <div className="onboarding-head">
          <div className="brand-mark" style={{ width: 44, height: 44, borderRadius: 12 }}>CC</div>
          <div>
            <h1 style={{ fontSize: 22, fontWeight: 700, margin: 0 }}>Welcome to {businessName}</h1>
            <p className="muted" style={{ margin: "4px 0 0", fontSize: 13 }}>Three quick steps to your first scheduled post.</p>
          </div>
        </div>
        <div className="onboarding-progress">
          <div className="onboarding-progress-bar"><span style={{ width: `${(doneCount / steps.length) * 100}%` }} /></div>
          <span className="muted" style={{ fontSize: 12 }}>{doneCount} of {steps.length} done</span>
        </div>
        <div className="onboarding-steps">
          {steps.map((step, index) => (
            <div key={step.key} className={`onboarding-step ${step.done ? "done" : ""}`}>
              <div className="onboarding-step-num">{step.done ? <Icon.Check width="16" height="16" /> : index + 1}</div>
              <div className="onboarding-step-body">
                <b>{step.title}</b>
                <small>{step.desc}</small>
              </div>
              <button className={`btn btn-sm ${step.done ? "" : "btn-primary"}`} onClick={() => setPage(step.go)}>{step.cta}</button>
            </div>
          ))}
        </div>
        <div className="onboarding-foot">
          <button className="btn" onClick={skip}>Skip for now</button>
          {doneCount === steps.length && <button className="btn btn-primary" onClick={() => setPage("create")}>You're all set — start posting</button>}
        </div>
      </div>
    </div>
  );
}

function LoginPage() {
  const hub = useClearCast();
  const [mode, setMode] = React.useState("login");
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const [name, setName] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState("");

  const submit = async (e) => {
    if (e) e.preventDefault();
    setError("");
    setBusy(true);
    try {
      if (mode === "login") await hub.actions.login(email.trim(), password);
      else await hub.actions.signup({ email: email.trim(), password, name: name.trim() });
    } catch (err) {
      setError(err.message || "Something went wrong");
    } finally { setBusy(false); }
  };

  return (
    <div className="review-shell">
      <form className="review-card" style={{ maxWidth: 420 }} onSubmit={submit}>
        <div className="review-head">
          <div className="brand-mark" style={{ width: 40, height: 40, borderRadius: 11 }}>CC</div>
          <div>
            <div style={{ fontWeight: 700, fontSize: 17 }}>ClearCast</div>
            <div className="muted" style={{ fontSize: 12 }}>{mode === "login" ? "Sign in to your workspace" : "Create your account"}</div>
          </div>
        </div>
        <button type="button" className="lp-google-btn" onClick={() => { window.location.href = "/api/auth/google"; }}>
          <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.76h3.56c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.56-2.76c-.98.66-2.23 1.06-3.72 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23z"/><path fill="#FBBC05" d="M5.84 14.11c-.22-.66-.35-1.36-.35-2.11s.13-1.45.35-2.11V7.05H2.18A11 11 0 0 0 1 12c0 1.77.42 3.45 1.18 4.95l3.66-2.84z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.05l3.66 2.84C6.71 7.29 9.14 5.38 12 5.38z"/></svg>
          Continue with Google
        </button>
        <div className="lp-or"><span>or</span></div>
        {mode === "signup" && <input className="input" placeholder="Your name" value={name} onChange={(e) => setName(e.target.value)} style={{ marginBottom: 10 }} />}
        <input className="input" type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} style={{ marginBottom: 10 }} autoFocus />
        <input className="input" type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} style={{ marginBottom: 10 }} />
        {error && <div style={{ color: "var(--danger)", fontSize: 12.5, marginBottom: 10 }}>{error}</div>}
        <button type="submit" className="btn btn-primary" disabled={busy || !email || !password} style={{ width: "100%", justifyContent: "center" }}>
          {busy ? "Please wait…" : mode === "login" ? "Sign in" : "Create account"}
        </button>
        <div style={{ textAlign: "center", marginTop: 14, fontSize: 12.5 }} className="muted">
          {mode === "login" ? "New here? " : "Already have an account? "}
          <button type="button" onClick={() => { setMode(mode === "login" ? "signup" : "login"); setError(""); }} style={{ color: "var(--accent-2)", fontWeight: 600 }}>
            {mode === "login" ? "Create an account" : "Sign in"}
          </button>
        </div>
      </form>
    </div>
  );
}

// Lands here after Google/OAuth round-trip: Supabase returns the session in the
// URL fragment (#access_token=...). We capture it synchronously (before any
// router effect can rewrite the hash), verify it server-side, then enter the app.
function AuthCallback() {
  const hub = useClearCast();
  const [parsed] = React.useState(() => {
    const frag = new URLSearchParams((window.location.hash || "").replace(/^#/, ""));
    return { token: frag.get("access_token"), error: frag.get("error_description") || frag.get("error") };
  });
  const [status, setStatus] = React.useState("working");
  const [message, setMessage] = React.useState("");

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      if (parsed.error) { setStatus("error"); setMessage(decodeURIComponent(parsed.error)); return; }
      if (!parsed.token) { setStatus("error"); setMessage("No sign-in details were returned. Please try again."); return; }
      try {
        await hub.actions.completeGoogleLogin(parsed.token);
        if (!cancelled) window.location.replace("/");
      } catch (err) {
        if (!cancelled) { setStatus("error"); setMessage(err.message || "We couldn't finish your Google sign-in."); }
      }
    })();
    return () => { cancelled = true; };
  }, []);

  return (
    <div className="review-shell">
      <div className="review-card" style={{ maxWidth: 380, textAlign: "center" }}>
        <div className="brand-mark" style={{ width: 44, height: 44, borderRadius: 12, margin: "0 auto 14px" }}>CC</div>
        {status === "working" ? (
          <>
            <div style={{ fontWeight: 700, fontSize: 16 }}>Signing you in…</div>
            <div className="muted" style={{ fontSize: 13, marginTop: 6 }}>Completing your Google sign-in.</div>
          </>
        ) : (
          <>
            <div style={{ fontWeight: 700, fontSize: 16 }}>Sign-in didn't finish</div>
            <div className="muted" style={{ fontSize: 13, marginTop: 6 }}>{message}</div>
            <button className="btn btn-primary" style={{ marginTop: 16 }} onClick={() => window.location.replace("/")}>Back to sign in</button>
          </>
        )}
      </div>
    </div>
  );
}

function ReviewPage({ token }) {
  const [state, setState] = React.useState({ loading: true });
  const [reviewer, setReviewer] = React.useState("");
  const [note, setNote] = React.useState("");
  const [busy, setBusy] = React.useState("");
  const [done, setDone] = React.useState("");

  const load = React.useCallback(async () => {
    try { const d = await ClearCastAPI.reviewGet(token); setState({ loading: false, data: d }); }
    catch (error) { setState({ loading: false, error: error.message || "This review link is no longer valid." }); }
  }, [token]);
  React.useEffect(() => { load(); }, [load]);

  const act = async (action) => {
    setBusy(action);
    try {
      await ClearCastAPI.reviewAction(token, { action, note, reviewer: reviewer.trim() || "Client reviewer" });
      setDone(action === "approve" ? "Approved — thank you!" : action === "reject" ? "Sent back as rejected." : "Changes requested.");
      setNote("");
      await load();
    } catch (error) { setState((s) => ({ ...s, error: error.message })); }
    finally { setBusy(""); }
  };

  if (state.loading) return <div className="review-shell"><div className="card" style={{ padding: 24 }}>Loading review…</div></div>;
  if (state.error) return <div className="review-shell"><div className="card" style={{ padding: 24 }}>{state.error}</div></div>;
  const { businessName, post } = state.data;
  const wl = state.data.whiteLabel;
  const markText = wl && wl.enabled && wl.appName ? wl.appName.slice(0, 2).toUpperCase() : (businessName || "CC").slice(0, 2).toUpperCase();
  const markColor = wl && wl.enabled && wl.accentColor ? wl.accentColor : (state.data.brandColor || "");
  const statusLabel = { needs_approval: "Awaiting your review", scheduled: "Approved & scheduled", changes_requested: "Changes requested", rejected: "Rejected", published: "Published" }[post.status] || post.status;

  return (
    <div className="review-shell">
      <div className="review-card">
        <div className="review-head">
          <div className="brand-mark" style={{ width: 36, height: 36, borderRadius: 10, background: markColor || undefined }}>{markText}</div>
          <div>
            <div style={{ fontWeight: 700 }}>{businessName}</div>
            <div className="muted" style={{ fontSize: 12 }}>asked you to review a post · {statusLabel}</div>
          </div>
        </div>

        <div className="review-post">
          <div style={{ display: "flex", gap: 6, marginBottom: 10, flexWrap: "wrap" }}>
            {(post.platforms || []).map((id) => <PlatformIcon key={id} id={id} size={20} />)}
            <span className="muted" style={{ fontSize: 12, marginLeft: 4 }}>{post.date} · {post.time}</span>
          </div>
          <div style={{ whiteSpace: "pre-wrap", fontSize: 14, lineHeight: 1.55 }}>{post.caption || "(no caption)"}</div>
          {(post.media || []).length > 0 && (
            <div className="muted" style={{ fontSize: 12, marginTop: 10 }}>{post.media.length} media attachment{post.media.length === 1 ? "" : "s"}</div>
          )}
        </div>

        {done ? (
          <div className="review-done">{done} <button className="btn btn-sm" onClick={() => setDone("")} style={{ marginLeft: 8 }}>Review again</button></div>
        ) : (
          <div className="review-actions">
            <input className="input" placeholder="Your name (optional)" value={reviewer} onChange={(e) => setReviewer(e.target.value)} />
            <textarea className="input" placeholder="Add a note or feedback (optional)" value={note} onChange={(e) => setNote(e.target.value)} style={{ minHeight: 64, resize: "vertical" }} />
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
              <button className="btn btn-primary" disabled={!!busy} onClick={() => act("approve")}>{busy === "approve" ? "Saving…" : "Approve"}</button>
              <button className="btn" disabled={!!busy} onClick={() => act("changes")}>{busy === "changes" ? "Saving…" : "Request changes"}</button>
              <button className="btn" disabled={!!busy} onClick={() => act("reject")} style={{ color: "var(--danger)" }}>{busy === "reject" ? "Saving…" : "Reject"}</button>
            </div>
          </div>
        )}
        <div className="muted" style={{ fontSize: 11, marginTop: 16, textAlign: "center" }}>No account needed — your review goes straight to the {businessName} team.</div>
      </div>
    </div>
  );
}

const PAGE_TITLES = {
  onboarding: "Get Started",
  create: "Create Post",
  calendar: "Calendar",
  published: "Published & Live",
  inbox: "Inbox",
  listening: "Listening",
  reviews: "Reviews",
  analytics: "Analytics",
  experiments: "A/B Tests",
  benchmarking: "Benchmarks",
  ads: "Ads",
  advocacy: "Advocacy & UGC",
  approvals: "Approvals",
  media: "Media Library",
  linkbio: "Link in Bio",
  links: "Short Links",
  reports: "Reports",
  bulk: "Bulk Upload",
  content: "Recycle & RSS",
  team: "Team",
  integrations: "Integrations",
  developer: "Developer / API",
  agency: "Agency & White-label",
  billing: "Billing & Plan",
  businesses: "Manage Businesses",
  status: "System Status",
  admin: "Admin Console",
  settings: "Settings",
};

function App() {
  const bioHandle = window.location.pathname.match(/^\/bio\/([^/]+)/)?.[1];
  const reviewToken = window.location.pathname.match(/^\/review\/([^/]+)/)?.[1];
  const initialPage = (() => {
    if (reviewToken) return "review-public";
    if (bioHandle) return "bio-public";
    const hash = window.location.hash.slice(1);
    if (PAGE_TITLES[hash]) return hash;
    return "create";
  })();
  const [page, setPage] = React.useState(initialPage);
  const [data, setData] = React.useState(null);
  const [workspaces, setWorkspaces] = React.useState({ workspaces: [], activeWorkspaceId: null });
  const [notice, setNotice] = React.useState("");
  const exportLockRef = React.useRef(false);

  const refresh = React.useCallback(async () => {
    const next = await ClearCastAPI.bootstrap();
    setData(next);
    if (next && Array.isArray(next.workspaces)) {
      setWorkspaces({ workspaces: next.workspaces, activeWorkspaceId: next.activeWorkspaceId });
    }
    return next;
  }, []);

  const toast = React.useCallback((message) => {
    setNotice(message);
    window.clearTimeout(window.__shareHubToastTimer);
    window.__shareHubToastTimer = window.setTimeout(() => setNotice(""), 2600);
  }, []);

  React.useEffect(() => {
    refresh().catch((error) => {
      console.error(error);
      toast("Could not load ClearCast data");
    });
  }, [refresh, toast]);

  React.useEffect(() => {
    if (page === "bio-public" || page === "review-public") return;
    if (window.location.pathname.startsWith("/auth/callback")) return;
    window.location.hash = page;
    window.scrollTo(0, 0);
  }, [page]);

  React.useEffect(() => {
    const onHash = () => {
      const h = window.location.hash.slice(1);
      if (PAGE_TITLES[h] && h !== page) setPage(h);
    };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  });

  // First-run: a brand-new/empty business (no channels, no posts) lands on the
  // guided onboarding checklist instead of a blank composer. Re-runs when the
  // active business changes. Respects a per-business "skip" flag.
  React.useEffect(() => {
    if (!data || !data.activeWorkspaceId || bioHandle) return;
    const empty = (data.integrations || []).filter((integration) => integration.connected).length === 0 && (data.posts || []).length === 0;
    let skipped = {};
    try { skipped = JSON.parse(window.localStorage.getItem("clearcast-onboard-skip") || "{}"); } catch (error) { skipped = {}; }
    if (empty && !skipped[data.activeWorkspaceId]) {
      setPage("onboarding");
    } else if (page === "onboarding" && (!empty)) {
      setPage("create");
    }
  }, [data && data.activeWorkspaceId]);

  const renderPage = () => {
    if (page === "bio-public") return <PublicBioPage handle={decodeURIComponent(bioHandle || "")} />;
    switch (page) {
      case "onboarding": return <OnboardingPage />;
      case "create": return <CreatePostPage />;
      case "calendar": return <CalendarPage />;
      case "published": return <PublishedPage />;
      case "inbox": return <InboxPage />;
      case "listening": return <ListeningPage />;
      case "reviews": return <ReviewsPage />;
      case "analytics": return <AnalyticsPage />;
      case "experiments": return <ExperimentsPage />;
      case "benchmarking": return <BenchmarkingPage />;
      case "ads": return <AdsPage />;
      case "advocacy": return <AdvocacyPage />;
      case "approvals": return <ApprovalsPage />;
      case "media": return <MediaLibraryPage />;
      case "linkbio": return <LinkInBioPage />;
      case "links": return <LinksPage />;
      case "reports": return <ReportsPage />;
      case "bulk": return <BulkUploadPage />;
      case "content": return <ContentPage />;
      case "team": return <TeamPage />;
      case "integrations": return <IntegrationsPage />;
      case "developer": return <DeveloperPage />;
      case "agency": return <AgencyPage />;
      case "billing": return <BillingPage />;
      case "businesses": return <BusinessesPage />;
      case "status": return <StatusPage />;
      case "admin": return <AdminPage />;
      case "settings": return <SettingsPage />;
      default: return <CreatePostPage />;
    }
  };

  const actions = React.useMemo(() => ({
    async createPost(payload) {
      const result = await ClearCastAPI.createPost(payload);
      setData(result.data);
      toast(payload.status === "draft" || payload.saveAsDraft ? "Draft saved" : payload.requireApproval === false ? "Post scheduled" : "Post sent for approval");
      return result.post;
    },
    async updatePost(id, payload) {
      const result = await ClearCastAPI.updatePost(id, payload);
      setData(result.data);
      toast("Post updated");
      return result.post;
    },
    async deletePost(id) {
      const result = await ClearCastAPI.deletePost(id);
      setData(result.data);
      toast("Post deleted");
      return result.deleted;
    },
    async duplicatePost(id) {
      const result = await ClearCastAPI.duplicatePost(id);
      setData(result.data);
      toast("Post duplicated as draft");
      return result.post;
    },
    async saveWorkspace(payload) {
      const result = await ClearCastAPI.saveWorkspace(payload);
      setData(result.data);
      toast("Settings saved");
      return result.workspace;
    },
    async resetDemoData() {
      const result = await ClearCastAPI.resetDemoData();
      setData(result.data);
      toast("Demo workspace reset");
      return result.data;
    },
    async uploadMedia(file, folder = "all") {
      const result = await ClearCastAPI.uploadMedia(file, folder);
      setData(result.data);
      toast("Media uploaded");
      return result.asset;
    },
    async updateMedia(id, payload) {
      const result = await ClearCastAPI.updateMedia(id, payload);
      setData(result.data);
      toast("Media updated");
      return result.asset;
    },
    async deleteMedia(id) {
      const result = await ClearCastAPI.deleteMedia(id);
      setData(result.data);
      toast("Media deleted");
      return result.deleted;
    },
    async bulkImport(csv) {
      const result = await ClearCastAPI.bulkImport(csv);
      setData(result.data);
      toast(`Imported ${result.imported} posts${result.skipped ? `, skipped ${result.skipped}` : ""}`);
      return result.posts;
    },
    async bulkValidate(csv) {
      const result = await ClearCastAPI.bulkValidate(csv);
      toast(result.invalidRows?.length ? `${result.invalidRows.length} rows need attention` : "CSV is ready to import");
      return result;
    },
    async aiCaption(caption, tone, voice = "") {
      const result = await ClearCastAPI.aiCaption(caption, tone, voice);
      toast(`AI caption generated · ${result.model || "workspace voice"}`);
      return result.caption;
    },
    async changePlan(plan) {
      const result = await ClearCastAPI.changePlan(plan);
      setData(result.data);
      toast("Plan updated");
      return result.billing;
    },
    async cancelBilling() {
      const result = await ClearCastAPI.cancelBilling();
      setData(result.data);
      toast("Subscription canceled — no further charges");
      return result.billing;
    },
    async reactivateBilling() {
      const result = await ClearCastAPI.reactivateBilling();
      setData(result.data);
      toast("Subscription reactivated");
      return result.billing;
    },
    async startCheckout(plan) {
      const result = await ClearCastAPI.billingCheckout(plan);
      if (result && result.url) { window.location.href = result.url; return result; }
      toast(result && result.message ? result.message : "Card payments aren't connected yet");
      return result;
    },
    async openBillingPortal() {
      const result = await ClearCastAPI.billingPortal();
      if (result && result.url) { window.location.href = result.url; return result; }
      toast(result && result.message ? result.message : "Card payments aren't connected yet");
      return result;
    },
    async setAiBudget(monthlyBudgetUsd) {
      const result = await ClearCastAPI.setAiBudget(monthlyBudgetUsd);
      setData(result.data);
      toast(monthlyBudgetUsd == null ? "AI budget cap removed" : `AI budget set to $${Number(monthlyBudgetUsd).toFixed(2)}/mo`);
      return result.usage;
    },
    async addStock(payload) {
      const result = await ClearCastAPI.addStock(payload);
      setData(result.data);
      toast("Stock photo added");
      return result.asset;
    },
    async addMediaLink(payload) {
      const result = await ClearCastAPI.addMediaLink(payload);
      setData(result.data);
      toast("Media added");
      return result.asset;
    },
    searchGiphy(q) {
      return ClearCastAPI.searchGiphy(q);
    },
    async generateImage(payload) {
      const result = await ClearCastAPI.generateImage(payload);
      setData(result.data);
      const spend = result.usage ? ` · $${result.usage.spentThisMonth.toFixed(2)} used this month` : "";
      toast((result.simulated ? "AI image generated (preview mode)" : `AI image generated (${result.provider})`) + spend);
      return result.asset;
    },
    async generateVariants(payload) {
      const result = await ClearCastAPI.generateVariants(payload);
      if (result.data) setData(result.data);
      toast(result.simulated ? "Tailored per-platform drafts (preview mode)" : `Tailored per-platform drafts (${result.provider})`);
      return result.variants || {};
    },
    async generateVideo(payload) {
      const result = await ClearCastAPI.generateVideo(payload);
      setData(result.data);
      const spend = result.usage ? ` · $${result.usage.spentThisMonth.toFixed(2)} used this month` : "";
      toast((result.simulated ? "AI video generated (preview mode)" : `AI video generated (${result.provider})`) + spend);
      return result.asset;
    },
    async hashtagSuggestions(payload) {
      const result = await ClearCastAPI.hashtagSuggestions(payload);
      return result.suggestions || [];
    },
    async updateHashtagGroup(id, payload) {
      const result = await ClearCastAPI.updateHashtagGroup(id, payload);
      setData(result.data);
      toast("Hashtag group saved");
      return result.group;
    },
    async createHashtagGroup(payload) {
      const result = await ClearCastAPI.createHashtagGroup(payload);
      setData(result.data);
      toast("Hashtag group created");
      return result.group;
    },
    async bestTimes(payload) {
      const result = await ClearCastAPI.bestTimes(payload);
      toast("Best-time recommendations refreshed");
      return result.recommendations || [];
    },
    async reply(messageId, text) {
      const result = await ClearCastAPI.reply(messageId, text);
      setData(result.data);
      toast("Reply sent");
      return result.message;
    },
    async updateInbox(messageId, payload, message = "Conversation updated") {
      const result = await ClearCastAPI.updateInbox(messageId, payload);
      setData(result.data);
      toast(message);
      return result.message;
    },
    async approve(postId) {
      const result = await ClearCastAPI.approve(postId);
      setData(result.data);
      toast("Post approved and scheduled");
      return result.post;
    },
    async login(email, password) {
      await ClearCastAPI.authLogin(email, password);
      await refresh();
      toast("Signed in");
    },
    async signup(payload) {
      await ClearCastAPI.authSignup(payload);
      await refresh();
      toast("Welcome to ClearCast");
    },
    async logout() {
      await ClearCastAPI.authLogout();
      await refresh();
      toast("Signed out");
    },
    async completeGoogleLogin(accessToken) {
      await ClearCastAPI.authGoogleToken(accessToken);
      await refresh();
      toast("Signed in with Google");
    },
    async setWhiteLabel(payload) {
      const result = await ClearCastAPI.setWhiteLabel(payload);
      setData(result.data);
      toast(payload.enabled ? "White-label branding saved" : "White-label turned off");
      return result.whiteLabel;
    },
    async addAdvocacy(payload) {
      const result = await ClearCastAPI.addAdvocacy(payload);
      setData(result.data);
      toast("Added to advocacy feed");
      return result.item;
    },
    async shareAdvocacy(id) {
      const result = await ClearCastAPI.shareAdvocacy(id);
      setData(result.data);
      toast("Marked as shared");
      return result.item;
    },
    async deleteAdvocacy(id) {
      const result = await ClearCastAPI.deleteAdvocacy(id);
      setData(result.data);
      toast("Removed");
      return result;
    },
    async addCreator(payload) {
      const result = await ClearCastAPI.addCreator(payload);
      setData(result.data);
      toast(`Added ${payload.name}`);
      return result.creator;
    },
    async updateCreator(id, payload) {
      const result = await ClearCastAPI.updateCreator(id, payload);
      setData(result.data);
      toast("Creator updated");
      return result.creator;
    },
    async deleteCreator(id) {
      const result = await ClearCastAPI.deleteCreator(id);
      setData(result.data);
      toast("Creator removed");
      return result;
    },
    async createAdCampaign(payload) {
      const result = await ClearCastAPI.createAdCampaign(payload);
      setData(result.data);
      toast("Campaign launched");
      return result.campaign;
    },
    async toggleAdCampaign(id) {
      const result = await ClearCastAPI.toggleAdCampaign(id);
      setData(result.data);
      toast(result.campaign.status === "active" ? "Campaign resumed" : "Campaign paused");
      return result.campaign;
    },
    async deleteAdCampaign(id) {
      const result = await ClearCastAPI.deleteAdCampaign(id);
      setData(result.data);
      toast("Campaign deleted");
      return result;
    },
    async addCompetitor(payload) {
      const result = await ClearCastAPI.addCompetitor(payload);
      setData(result.data);
      toast(`Tracking ${payload.name}`);
      return result.competitor;
    },
    async deleteCompetitor(id) {
      const result = await ClearCastAPI.deleteCompetitor(id);
      setData(result.data);
      toast("Competitor removed");
      return result;
    },
    async respondToReview(id, text) {
      const result = await ClearCastAPI.respondToReview(id, text);
      setData(result.data);
      toast("Response posted");
      return result.review;
    },
    async addListening(payload) {
      const result = await ClearCastAPI.addListening(payload);
      setData(result.data);
      toast(`Now tracking "${payload.keyword}"`);
      return result.stream;
    },
    async refreshListening(id) {
      const result = await ClearCastAPI.refreshListening(id);
      setData(result.data);
      toast("Mentions refreshed");
      return result.stream;
    },
    async deleteListening(id) {
      const result = await ClearCastAPI.deleteListening(id);
      setData(result.data);
      toast("Stopped tracking");
      return result;
    },
    async createApiKey(payload) {
      const result = await ClearCastAPI.createApiKey(payload);
      setData(result.data);
      toast("API key created");
      return result.apiKey;
    },
    async deleteApiKey(id) {
      const result = await ClearCastAPI.deleteApiKey(id);
      setData(result.data);
      toast("API key revoked");
      return result;
    },
    async addWebhook(payload) {
      const result = await ClearCastAPI.addWebhook(payload);
      setData(result.data);
      toast("Webhook added");
      return result.webhook;
    },
    async deleteWebhook(id) {
      const result = await ClearCastAPI.deleteWebhook(id);
      setData(result.data);
      toast("Webhook removed");
      return result;
    },
    async testWebhook(id) {
      await ClearCastAPI.testWebhook(id);
      toast("Test event sent");
    },
    async addEvergreen(payload) {
      const result = await ClearCastAPI.addEvergreen(payload);
      setData(result.data);
      toast("Added to evergreen library");
      return result.item;
    },
    async deleteEvergreen(id) {
      const result = await ClearCastAPI.deleteEvergreen(id);
      setData(result.data);
      toast("Removed from evergreen");
      return result;
    },
    async queueEvergreen(id) {
      const result = await ClearCastAPI.queueEvergreen(id);
      setData(result.data);
      toast("Recycled into a draft post");
      return result.post;
    },
    async addRssFeed(payload) {
      const result = await ClearCastAPI.addRssFeed(payload);
      setData(result.data);
      toast("Feed added");
      return result.feed;
    },
    async deleteRssFeed(id) {
      const result = await ClearCastAPI.deleteRssFeed(id);
      setData(result.data);
      toast("Feed removed");
      return result;
    },
    async importRssFeed(id) {
      const result = await ClearCastAPI.importRssFeed(id);
      setData(result.data);
      toast(result.imported ? `Imported ${result.imported} draft${result.imported === 1 ? "" : "s"} from feed` : (result.note || "No new items"));
      return result;
    },
    async createExperiment(payload) {
      const result = await ClearCastAPI.createExperiment(payload);
      setData(result.data);
      toast("A/B test started");
      return result.experiment;
    },
    async concludeExperiment(id) {
      const result = await ClearCastAPI.concludeExperiment(id);
      setData(result.data);
      toast(`Test concluded — variant ${result.experiment.winner} won`);
      return result.experiment;
    },
    async deleteExperiment(id) {
      const result = await ClearCastAPI.deleteExperiment(id);
      setData(result.data);
      toast("Test deleted");
      return result;
    },
    async saveSavedReply(payload) {
      const result = await ClearCastAPI.saveSavedReply(payload);
      setData(result.data);
      toast("Saved reply added");
      return result.reply;
    },
    async deleteSavedReply(id) {
      const result = await ClearCastAPI.deleteSavedReply(id);
      setData(result.data);
      toast("Saved reply removed");
      return result;
    },
    async createLink(payload) {
      const result = await ClearCastAPI.createLink(payload);
      setData(result.data);
      const url = window.location.origin + result.shortPath;
      try { await navigator.clipboard.writeText(url); toast("Short link created & copied"); }
      catch (error) { toast("Short link created"); }
      return result;
    },
    async deleteLink(id) {
      const result = await ClearCastAPI.deleteLink(id);
      setData(result.data);
      toast("Link deleted");
      return result;
    },
    async getReviewLink(postId) {
      const result = await ClearCastAPI.reviewLink(postId);
      const url = window.location.origin + result.path;
      try { await navigator.clipboard.writeText(url); toast("Client review link copied — no login needed"); }
      catch (error) { toast(`Review link: ${url}`); }
      return url;
    },
    async approvalAction(postId, action, note = "") {
      const result = await ClearCastAPI.approvalAction(postId, action, note);
      setData(result.data);
      const labels = { approve: "Post approved and scheduled", reject: "Post rejected", changes: "Changes requested" };
      toast(labels[action] || "Approval updated");
      return result.post;
    },
    async approvalComment(postId, text) {
      const result = await ClearCastAPI.approvalComment(postId, text);
      setData(result.data);
      toast("Comment added");
      return result.comment;
    },
    async publishPost(postId, retry = false) {
      try {
        const result = retry ? await ClearCastAPI.retryPublish(postId) : await ClearCastAPI.publishPost(postId);
        setData(result.data);
        toast(result.attempt?.status === "blocked" ? "Publishing paused by outage policy" : "Post published");
        return result.post;
      } catch (error) {
        const fresh = await ClearCastAPI.bootstrap();
        setData(fresh);
        toast(retry ? "Retry failed readiness checks" : "Publish failed readiness checks");
        throw error;
      }
    },
    async runDuePublishQueue() {
      const result = await ClearCastAPI.runDuePublishQueue();
      setData(result.data);
      toast(`Queue run complete: ${result.published} published, ${result.failed} failed${result.blocked ? `, ${result.blocked} blocked` : ""}`);
      return result;
    },
    async updateTask(id, payload) {
      const result = await ClearCastAPI.updateTask(id, payload);
      setData(result.data);
      toast(payload.dismissed ? "Task dismissed" : payload.snoozedUntil ? "Task snoozed" : "Task updated");
      return result.task;
    },
    async restoreTask(id) {
      const result = await ClearCastAPI.restoreTask(id);
      setData(result.data);
      toast("Task restored");
      return result.restored;
    },
    async addBioLink(title, url) {
      const result = await ClearCastAPI.addBioLink(title, url);
      setData(result.data);
      toast("Link added");
      return result.link;
    },
    async saveBioPage(payload) {
      const result = await ClearCastAPI.saveBioPage(payload);
      setData(result.data);
      toast("Bio page saved");
      return result.page;
    },
    async updateBioLink(id, payload) {
      const result = await ClearCastAPI.updateBioLink(id, payload);
      setData(result.data);
      toast("Bio link updated");
      return result.link;
    },
    async deleteBioLink(id) {
      const result = await ClearCastAPI.deleteBioLink(id);
      setData(result.data);
      toast("Bio link deleted");
      return result.deleted;
    },
    async clickBioLink(id) {
      const result = await ClearCastAPI.clickBioLink(id);
      setData(result.data);
      toast("Preview click recorded");
      return result.link;
    },
    async connectIntegration(id) {
      const result = await ClearCastAPI.connectIntegration(id);
      if (result.oauthUrl) { window.location.href = result.oauthUrl; return null; }
      setData(result.data);
      toast(result.demo ? "Channel connected (demo — add platform keys for live posting)" : "Channel connected");
      return result.integration;
    },
    async toggleIntegration(id) {
      const result = await ClearCastAPI.toggleIntegration(id);
      setData(result.data);
      toast(result.integration?.connected ? "Integration connected" : "Integration disconnected");
      return result.integration;
    },
    async updateIntegration(id, payload) {
      const result = await ClearCastAPI.updateIntegration(id, payload);
      setData(result.data);
      toast("Integration updated");
      return result.integration;
    },
    async syncIntegration(id) {
      const result = await ClearCastAPI.syncIntegration(id);
      setData(result.data);
      toast("Integration sync complete");
      return result.integration;
    },
    async reconnectIntegration(id) {
      const result = await ClearCastAPI.reconnectIntegration(id);
      setData(result.data);
      toast("Integration reconnected");
      return result.integration;
    },
    async createReport(payload) {
      const result = await ClearCastAPI.createReport(payload);
      setData(result.data);
      toast("Report created");
      return result.report;
    },
    exportReports(filters = {}) {
      if (exportLockRef.current) return;
      exportLockRef.current = true;
      window.location.href = ClearCastAPI.exportUrl(filters);
      toast("Report export started");
      window.setTimeout(() => {
        exportLockRef.current = false;
      }, 1200);
    },
    async switchUser(userId) {
      const result = await ClearCastAPI.switchUser(userId);
      setData(result.data);
      toast(`Viewing as ${result.user.name}`);
      return result.user;
    },
    async inviteMember(payload) {
      const result = await ClearCastAPI.inviteMember(payload);
      setData(result.data);
      toast("Invite sent");
      return result.invite;
    },
    async updateMember(id, payload) {
      const result = await ClearCastAPI.updateMember(id, payload);
      setData(result.data);
      toast("Member access updated");
      return result.member;
    },
    async updateRole(id, permissions) {
      const result = await ClearCastAPI.updateRole(id, permissions);
      setData(result.data);
      toast("Role permissions saved");
      return result.role;
    },
    async resendInvite(id) {
      const result = await ClearCastAPI.resendInvite(id);
      setData(result.data);
      toast("Invite resent");
      return result.invite;
    },
    async switchWorkspace(id) {
      const result = await ClearCastAPI.switchWorkspace(id);
      setWorkspaces({ workspaces: result.workspaces, activeWorkspaceId: result.activeWorkspaceId });
      await refresh();
      const name = result.workspaces.find((workspace) => workspace.id === id)?.name || "business";
      toast(`Switched to ${name}`);
      return result;
    },
    async createWorkspace(payload) {
      const result = await ClearCastAPI.createWorkspace(payload);
      setWorkspaces({ workspaces: result.workspaces, activeWorkspaceId: result.activeWorkspaceId });
      await refresh();
      toast(`${payload.name} added`);
      return result;
    },
    async renameWorkspace(id, payload) {
      const result = await ClearCastAPI.renameWorkspace(id, payload);
      setWorkspaces({ workspaces: result.workspaces, activeWorkspaceId: result.activeWorkspaceId });
      await refresh();
      toast("Business updated");
      return result;
    },
    async deleteWorkspace(id) {
      const result = await ClearCastAPI.deleteWorkspace(id);
      setWorkspaces({ workspaces: result.workspaces, activeWorkspaceId: result.activeWorkspaceId });
      await refresh();
      toast("Business removed");
      return result;
    },
    refresh
  }), [refresh, toast]);

  React.useEffect(() => {
    const isNavClick = (target) => target.closest(".sidebar .nav-item");
    const isFormControl = (target) => ["INPUT", "TEXTAREA", "SELECT", "OPTION"].includes(target.tagName);
    const textOf = (node) => (node.getAttribute("aria-label") || node.getAttribute("title") || node.innerText || node.textContent || "").replace(/\s+/g, " ").trim();
    const activateTab = (button) => {
      const group = button.closest(".tabs");
      if (!group) return;
      group.querySelectorAll(".tab").forEach((tab) => tab.classList.remove("active"));
      button.classList.add("active");
    };
    const show = (message) => {
      if (message) toast(message);
    };

    const onClick = async (event) => {
      const target = event.target;
      if (isFormControl(target) || isNavClick(target)) return;

      const link = target.closest("a");
      if (link && link.getAttribute("href")?.startsWith("/api/")) {
        return;
      }

      const button = target.closest("button, a");
      if (!button) return;
      const text = textOf(button);
      if (!text) return;

      if (button.classList.contains("tab")) {
        activateTab(button);
        show(`${text.replace(/[0-9]/g, "").trim()} view selected`);
        return;
      }

      if (text === "New Post") {
        setPage("create");
        show("Composer opened");
        return;
      }

      if (text.includes("Export") || text === "CSV") {
        actions.exportReports();
        return;
      }

      // Note: previously a long list of button-text fragments fired reassuring
      // "success" toasts even when nothing actually happened. That was removed —
      // every action must be wired to real behavior with a truthful result, or
      // show nothing. Buttons not yet wired simply do nothing here.
    };

    document.addEventListener("click", onClick);
    return () => document.removeEventListener("click", onClick);
  }, [actions, setPage, toast]);

  if (page === "review-public") {
    return (
      <ClearCastContext.Provider value={{ data, actions, toast, setPage }}>
        <ReviewPage token={decodeURIComponent(reviewToken || "")} />
        <div className={`toast ${notice ? "show" : ""}`}>{notice}</div>
      </ClearCastContext.Provider>
    );
  }

  // OAuth (Google) return leg — capture the session from the URL fragment.
  if (window.location.pathname.startsWith("/auth/callback")) {
    return (
      <ClearCastContext.Provider value={{ data, actions, toast, setPage }}>
        <AuthCallback />
        <div className={`toast ${notice ? "show" : ""}`}>{notice}</div>
      </ClearCastContext.Provider>
    );
  }

  // Public front door (only in password mode, and once data has loaded telling
  // us so): a branded marketing landing with features, pricing packages, and an
  // integrated sign-up / log-in / purchase flow. Falls back to the simple
  // LoginPage if the landing bundle failed to load.
  if (data && data.authMode === "password" && !data.authUser) {
    const FrontDoor = typeof LandingPage === "function" ? LandingPage : LoginPage;
    return (
      <ClearCastContext.Provider value={{ data, actions, toast, setPage }}>
        <FrontDoor />
        <div className={`toast ${notice ? "show" : ""}`}>{notice}</div>
      </ClearCastContext.Provider>
    );
  }

  return (
    <ClearCastContext.Provider value={{ data, actions, toast, setPage }}>
      <div className="app" data-screen-label={PAGE_TITLES[page] || "Bio"}>
        {page !== "bio-public" && <Sidebar page={page} setPage={setPage} data={data} workspaces={workspaces.workspaces} activeWorkspaceId={workspaces.activeWorkspaceId} actions={actions}/>}
        <div className="main">
          {page !== "bio-public" && <Topbar title={PAGE_TITLES[page]} page={page} data={data} actions={actions} setPage={setPage} workspaces={workspaces.workspaces} activeWorkspaceId={workspaces.activeWorkspaceId}/>}
          {data ? renderPage() : <div className="page"><div className="card">Loading ClearCast...</div></div>}
        </div>
        <div className={`toast ${notice ? "show" : ""}`}>{notice}</div>
      </div>
    </ClearCastContext.Provider>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
window.useClearCast = useClearCast;
window.useShareHub = useClearCast;
