const STEPS = [
  { id: 'teams', label: 'Your teams' },
  { id: 'cadence', label: 'Newsletters & cadence' },
  { id: 'leagues', label: "What's next" },
  { id: 'signals', label: 'Signals & lens' },
  { id: 'confirm', label: 'Confirm' },
];

function OnboardingPage({ seedEmail = '', manageMode = false, onNav = () => {} }) {
  const { isPhone } = window.useIsMobile();
  const [i, setI] = React.useState(0);
  const [done, setDone] = React.useState(false);
  const [op, setOp] = React.useState(1);
  const [v, setV] = React.useState({
    email: seedEmail, teams: [], teamCadence: {}, teamOn: {},
    league: { on: true, cadence: 'daily' }, weeklies: { power: true }, topPicks: false,
    // source left '' even in manage mode: /signup preserves a prior source only when the incoming
    // one is falsy, so a truthy 'manage' would clobber the subscriber's original acquisition source.
    topics: [], intent: '', source: '', leagues: {},
    // Ad attribution (#4): utm_* captured from the landing URL on load (window.synthUtm), stored in
    // preferences.utm. Honeypot (#3): a hidden field a human never fills; a bot that auto-fills it
    // is rejected server-side. Both ride in the /lead and /signup payloads.
    utm: (window.synthUtm || {}), hp_url: '',
  });
  // Tracks the manage prefill outcome: null = unresolved (not fetched yet, OR a DB-blip ok:false),
  // true = a real subscriber row was found + seeded, false = the email is not a known subscriber.
  // Drives ONLY the banner copy, not the save gate — an unknown-email save still goes through
  // /signup and creates a row (intended, per the "saving will create one" banner).
  const [manageFound, setManageFound] = React.useState(manageMode ? null : true);
  const [manageUnsub, setManageUnsub] = React.useState(false);

  // MANAGE MODE: prefill the wizard from the subscriber's CURRENT preferences so this is an EDIT,
  // not a blank signup. Fetch /preferences once on mount for the seeded email (URL or remembered).
  // Seed EVERY field /signup's upsert overwrites — teams, cadence, league AND weeklies/topPicks/
  // topics/intent/leagues — so a save is identity on anything the reader didn't touch (without the
  // latter group a save would silently reset them to wizard defaults and, via weeklies, flip real
  // Ghost send labels). Only apply when the endpoint FOUND the row; on ok:false (DB error) or
  // not-found, leave the wizard at its defaults and record found=false so save is gated.
  const [prefilled, setPrefilled] = React.useState(false);
  React.useEffect(() => {
    if (!manageMode || prefilled) return;
    const em = (seedEmail || '').trim();
    if (!/.+@.+\..+/.test(em)) return;   // no email yet — the email step will collect one
    const signup = window.SYNTH_SIGNUP_URL || '/signup';
    const base = window.SYNTH_PREFERENCES_URL
      || (/\/signup$/.test(signup) ? signup.replace(/\/signup$/, '/preferences') : '/preferences');
    fetch(base + '?email=' + encodeURIComponent(em))
      .then(r => r.json())
      .then(d => {
        if (!d || !d.ok) return;            // DB error (ok:false): leave defaults, don't record found
        if (!d.found) { setManageFound(false); return; }  // unknown email: don't seed empties over defaults
        setManageFound(true);
        setManageUnsub(!!d.unsubscribed);
        setV(prev => Object.assign({}, prev, {
          email: em,
          teams: Array.isArray(d.team_names) ? d.team_names : prev.teams,
          teamCadence: (d.cadence_by_name && typeof d.cadence_by_name === 'object') ? d.cadence_by_name : prev.teamCadence,
          teamOn: (d.cadence_by_name && typeof d.cadence_by_name === 'object')
            ? Object.keys(d.cadence_by_name).reduce((o, k) => { o[k] = true; return o; }, {})
            : prev.teamOn,
          league: (d.league && typeof d.league === 'object')
            ? { on: !!d.league.on, cadence: d.league.cadence === 'mwf' ? 'mwf' : 'daily' }
            : prev.league,
          // round-trip the rest so a save doesn't wipe them
          weeklies: (d.weeklies && typeof d.weeklies === 'object') ? d.weeklies : prev.weeklies,
          topPicks: typeof d.topPicks === 'boolean' ? d.topPicks : prev.topPicks,
          topics: Array.isArray(d.topics) ? d.topics : prev.topics,
          intent: typeof d.intent === 'string' ? d.intent : prev.intent,
          leagues: (d.leagues && typeof d.leagues === 'object') ? d.leagues : prev.leagues,
        }));
      })
      .catch(() => {})
      .finally(() => setPrefilled(true));
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  const set = (patch) => setV(prev => Object.assign({}, prev, patch));
  const update = (fn) => setV(fn);
  const move = (next) => {
    setOp(0);
    setTimeout(() => { setI(next); window.scrollTo(0, 0); setOp(1); }, 180);
  };
  const emailOk = /.+@.+\..+/.test(v.email);
  // Teams are OPTIONAL now: step 0 is always passable (a no-team subscriber is valid).
  // Only the final step still gates on a valid email.
  const valid = i === STEPS.length - 1 ? emailOk : true;
  const pct = done ? 100 : Math.round((i / (STEPS.length - 1)) * 100);

  // Capture the email as a LEAD the moment the wizard opens with a seeded email (entered on
  // the homepage hero/CTA), so a drop-off before finishing team selection doesn't lose it.
  // Fires once; best-effort (ignore failures — the full /signup will still upsert later).
  React.useEffect(() => {
    if (!emailOk) return;
    if (manageMode) return;   // an existing subscriber editing prefs is not a new lead — don't POST /lead
    // Prefer an explicit SYNTH_LEAD_URL; only fall back to deriving from SYNTH_SIGNUP_URL when
    // it ends in /signup. If neither yields a real /lead URL, skip (don't POST a LeadPayload to
    // /signup, which would create a premature "subscribed" row).
    const signup = window.SYNTH_SIGNUP_URL || '/signup';
    const leadUrl = window.SYNTH_LEAD_URL
      || (/\/signup$/.test(signup) ? signup.replace(/\/signup$/, '/lead') : null);
    if (!leadUrl) return;
    fetch(leadUrl, {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: v.email, source: v.source || 'wizard', utm: v.utm, hp_url: v.hp_url }),
    }).catch(() => {});
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Wire the (previously stubbed) submit to the Synth signup backend. UI/flow unchanged.
  // POST the payload; on a transient failure retry once silently (handles a serverless-DB
  // cold start). Then advance to Will's existing success screen. NOTE: Will's design has no
  // error/retry state, so a persistent failure still shows success — a real error UI is a
  // pre-launch item that needs Will's design (see docs/PHASES.md). The backend logs any
  // failed write so it isn't invisible to us.
  const submit = () => {
    const url = window.SYNTH_SIGNUP_URL || '/signup';
    const opts = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(v) };
    // Remember this email on THIS browser so a later #manage visit prefills without typing
    // (per-device convenience; see Site.jsx rememberEmail). Best-effort via the global if present.
    try { if (window.synthRememberEmail) window.synthRememberEmail(v.email); } catch (e) { /* noop */ }
    const attempt = (retriesLeft) => fetch(url, opts).then(res => {
      if (res.ok) { setDone(true); return; }
      if (retriesLeft > 0 && res.status >= 500) { attempt(retriesLeft - 1); return; }
      setDone(true);
    }).catch(() => { retriesLeft > 0 ? attempt(retriesLeft - 1) : setDone(true); });
    attempt(1);
  };

  const body = done ? <StepDone v={v} onNav={onNav} manageMode={manageMode} />
    : i === 0 ? <StepTeams v={v} set={set} update={update} />
    : i === 1 ? <StepNewsletters v={v} set={set} />
    : i === 2 ? <StepLeagues v={v} set={set} />
    : i === 3 ? <StepSignals v={v} set={set} />
    : <StepConfirm v={v} set={set} onEdit={move} />;

  return (
    <main style={{ background: 'var(--syn-paper)', minHeight: 'calc(100vh - 64px)', display: 'flex', flexDirection: 'column' }}>
      {/* Honeypot (#3): off-screen, aria-hidden, not a real tab stop. A human never fills it; a bot
          that auto-fills every field submits a non-empty hp_url and is rejected server-side.
          Named 'hp_url' (NOT 'website'/'url'/'name'/'email') so password managers / browser autofill
          don't populate it and false-reject a real user. autoComplete off as a second guard. */}
      <input type="text" name="hp_url" tabIndex={-1} autoComplete="off" aria-hidden="true"
        value={v.hp_url} onChange={e => set({ hp_url: e.target.value })}
        style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, opacity: 0 }} />
      <div style={{ height: 2, background: 'var(--syn-rule)', width: '100%' }}>
        <div style={{ height: '100%', width: pct + '%', background: 'var(--syn-navy)', transition: `width 420ms ${EASE}` }} />
      </div>
      <div style={{ flex: 1, padding: isPhone ? '28px 18px 80px' : '42px 48px 96px' }}>
        <div style={{ maxWidth: 900, margin: '0 auto' }}>
          {manageMode && !done ? (
            <div style={{ marginBottom: 20, padding: '12px 16px', background: 'var(--syn-paper-2)', border: '1px solid var(--syn-rule)', borderRadius: 'var(--syn-radius-md)', fontFamily: 'var(--syn-font-body)', fontSize: 14, color: 'var(--syn-fg-muted)' }}>
              <strong style={{ color: 'var(--syn-navy)' }}>Managing your preferences</strong>{v.email ? ` for ${v.email}` : ''}.{' '}
              {manageFound === false
                ? "We don't have a subscription for that email yet — saving will create one."
                : manageFound === true
                  ? (manageUnsub
                      ? 'You previously unsubscribed; saving will re-subscribe you with these preferences.'
                      : 'Change your teams or cadence below, then save — you stay subscribed, just updated.')
                  // null: not resolved (no email entered yet, or a transient read issue) — stay neutral
                  // rather than promise "just updated" over a wizard that may be sitting at defaults.
                  : 'Update your teams or cadence below, then save.'}
            </div>
          ) : null}
          <div style={{ opacity: op, transition: `opacity 180ms ${EASE}` }}>{body}</div>
        </div>
      </div>
      {done ? null : (
        <div style={{ position: 'sticky', bottom: 0, background: 'var(--syn-paper)', borderTop: '1px solid var(--syn-rule)', padding: isPhone ? '14px 18px' : '16px 48px', zIndex: 5 }}>
          {i === 0 && v.teams.length === 0 ? (
            <div style={{ maxWidth: 900, margin: '0 auto 10px', fontFamily: 'var(--syn-font-body)', fontSize: 13.5, color: 'var(--syn-fg-subtle)' }}>
              Pick a team to get your daily briefing — or continue without one and add teams anytime later.
            </div>
          ) : null}
          <div style={{ maxWidth: 900, margin: '0 auto', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: isPhone ? 8 : 16, flexWrap: isPhone ? 'wrap' : 'nowrap' }}>
            <button type="button" onClick={() => (i === 0 ? onNav('home') : move(i - 1))} style={{
              background: 'none', border: '2px solid var(--syn-rule)', borderRadius: 'var(--syn-radius-md)', padding: '12px 20px',
              fontFamily: 'var(--syn-font-body)', fontSize: 14.5, color: 'var(--syn-fg-muted)', cursor: 'pointer',
            }}>{i === 0 ? 'Cancel' : 'Back'}</button>
            <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
              {i === 1 ? (
                <React.Fragment>
                  <button type="button" onClick={() => move(STEPS.length - 1)} style={{
                    background: 'var(--syn-navy)', color: 'var(--syn-paper)',
                    border: 0, borderRadius: 'var(--syn-radius-md)', padding: '13px 26px',
                    fontFamily: 'var(--syn-font-body)', fontSize: 15, fontWeight: 500, cursor: 'pointer',
                    transition: `background-color 200ms ${EASE}`,
                  }}>I'm done</button>
                  <button type="button" onClick={() => move(2)} style={{ background: 'var(--syn-paper-2)', border: '2px solid var(--syn-rule)', borderRadius: 'var(--syn-radius-md)', padding: '13px 20px', fontFamily: 'var(--syn-font-body)', fontSize: 14.5, color: 'var(--syn-fg-muted)', cursor: 'pointer' }}>Tell us more about you</button>
                </React.Fragment>
              ) : (
                <React.Fragment>
              {i > 0 && i < STEPS.length - 1 ? (
                <button type="button" onClick={() => move(i + 1)} style={{ background: 'none', border: 0, padding: 0, fontFamily: 'var(--syn-font-body)', fontSize: 14, color: 'var(--syn-fg-subtle)', cursor: 'pointer' }}>Skip</button>
              ) : null}
              <button type="button" disabled={!valid} onClick={() => (i === STEPS.length - 1 ? submit() : move(i + 1))} style={{
                background: valid ? 'var(--syn-navy)' : 'var(--syn-paper-3)', color: valid ? 'var(--syn-paper)' : 'var(--syn-fg-subtle)',
                border: 0, borderRadius: 'var(--syn-radius-md)', padding: '13px 26px',
                fontFamily: 'var(--syn-font-body)', fontSize: 15, fontWeight: 500, cursor: valid ? 'pointer' : 'not-allowed',
                transition: `background-color 200ms ${EASE}`,
              }}>{i === STEPS.length - 1 ? (manageMode ? 'Save preferences' : 'Confirm and subscribe') : 'Continue'}</button>
                </React.Fragment>
              )}
            </div>
          </div>
        </div>
      )}
    </main>
  );
}
window.OnboardingPage = OnboardingPage;
