import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import SignIn from './SignIn.jsx';
import Onboarding from './Onboarding.jsx';
import { useSession } from './useSession.js';
import { useProfile } from './useProfile.js';
import { isConfigured, missingConfig } from './supabase.js';
import { isPreview, demoSession } from './demo.js';
import Button from './ui/Button.jsx';
import '../packages/core/design-tokens.css';

/* CAPTURE AN INVITE BEFORE ANYTHING ELSE DECIDES WHAT TO RENDER.

   An invite link is opened by somebody who has no session and no profile yet - that is the whole
   situation an invite exists for. But the component that USES the token only mounts once both of
   those exist, so by the time it looked, the query string was long gone: the person signed in,
   finished onboarding, and arrived at an empty Rooms list with the invitation silently dropped.

   Reading it here, at module load, is the only place guaranteed to run before every branch below
   picks a screen. Whoever ends up rendering, the token is waiting for them. */
try {
  const invited = new URLSearchParams(window.location.search).get('join');
  if (invited) window.localStorage.setItem('eg.join', invited);
} catch { /* storage can be unavailable; the ordinary sign-in path still works */ }

/**
 * Renders a plain set-up notice instead of the app when the deployment is not
 * configured. This replaces the previous behaviour (throwing at import time),
 * which showed users a blank white screen with no explanation.
 */
function SetupNotice({ missing }) {
  return (
    <main className="eg-center">
      <section className="eg-card eg-panel">
        <div className="eg-panel__body">
          <h1 className="eg-title">Expense Guru needs configuring</h1>
          <p className="eg-body eg-mute">
            This build is missing its environment variables, so it cannot reach the database.
          </p>
          <div className="eg-code-block">
            {missing.map(name => <div key={name}>{name}</div>)}
          </div>
          <p className="eg-cap eg-mute">
            Set them in your hosting provider (Vercel: Settings, Environment Variables), then redeploy.
            Vite inlines these values at build time.
          </p>
        </div>
      </section>
    </main>
  );
}

/** Neutral while the session or the profile resolves. Never show the sign-in
 *  form here: a returning user following a magic link would see it flash. */
function Resolving() {
  return (
    <main className="eg-center">
      <p className="eg-body eg-mute">Just a moment.</p>
    </main>
  );
}

/**
 * A failed profile lookup is NOT the same as "no profile". Showing onboarding
 * here would walk an existing user back through first-run setup, so offer a
 * retry instead.
 */
function ProfileProblem({ onRetry }) {
  return (
    <main className="eg-center">
      <section className="eg-card eg-panel">
        <div className="eg-panel__body">
          <div className="eg-panel__head">
            <h1 className="eg-title">We could not load your account</h1>
            <p className="eg-body eg-mute">That is usually a connection blip. Try once more.</p>
          </div>
          <Button variant="cta" block onClick={onRetry}>Try again</Button>
        </div>
      </section>
    </main>
  );
}

/**
 * Session -> profile -> app.
 *
 * A signed-in account with no user_profiles row is NEW and gets onboarding.
 * That row is written by onboarding, which is what makes the account "known"
 * from then on. The decision is a fact about the data, not a guess.
 */
function Root() {
  /* THE DASHBOARD FIRST, WHILE THE PRODUCT IS BEING DESIGNED.
   *
   * Checked BEFORE the session hook, so nothing is requested and no sign-in form can flash
   * on the way past. In development this is on without asking; `?auth=1` turns it off when
   * the sign-in flow itself is what is being worked on.
   *
   * A DEPLOYED BUILD IS UNCHANGED. isPreview is false there, so the session hook runs and a
   * stranger still meets the sign-in screen - which is the whole point, because a dashboard
   * of somebody else's imaginary money is worse than a login prompt. */
  if (isPreview()) {
    /* Marked, so a screenshot or a bug report can never be mistaken for real data. */
    document.documentElement.dataset.preview = 'demo';
    return <App session={demoSession} />;
  }

  const session = useSession();
  const { loading, profile, error, reload } = useProfile(session);

  if (session === undefined) return <Resolving />;
  if (!session) return <SignIn />;
  if (loading) return <Resolving />;
  if (error) return <ProfileProblem onRetry={reload} />;
  if (!profile) return <Onboarding session={session} onDone={reload} />;
  return <App session={session} />;
}

/* THE APP'S FILES, AVAILABLE OFFLINE (docs/OFFLINE_PLAN.md, tier 1). Registered only where it helps and
   cannot hurt: a real production build, not the demo, and not inside a native shell - the Capacitor shells
   already carry the files on the phone, and a second cache there would only be a second thing to go stale. */
const inNativeShell = Boolean(window.Capacitor && window.Capacitor.isNativePlatform && window.Capacitor.isNativePlatform());
if ('serviceWorker' in navigator && import.meta.env.PROD && !isPreview() && !inNativeShell) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js?v=' + __BUILD_ID__).catch(() => { /* offline support is a bonus, never a blocker */ });
  });
}

const root = createRoot(document.getElementById('root'));
root.render(isConfigured ? <Root /> : <SetupNotice missing={missingConfig} />);
