/* Pre-Sales Agent — onboarding. A breeze: auto-researches your company from
   your work email, then asks you to confirm ONE thing — your selling profile,
   including who you sell to (ICP). That ICP is what lets the agent judge
   whether each prospect is actually a good fit. */

// Role picker: grouped (Executive / Sales / Marketing) + free-text "Other".
// The role is injected into the seller baseline on every brief, so a precise
// title lets the agent tailor the angle to who's actually in the room.
const ROLE_GROUPS = [
  { label: "Executive / Leadership", roles: [
    "Founder / CEO",
    "Co-Founder",
    "President / COO",
    "Chief Revenue Officer (CRO)",
    "Chief Commercial Officer (CCO)",
    "VP of Sales",
    "VP of Revenue / RevOps",
    "General Manager",
    "Managing Director / Partner",
  ] },
  { label: "Sales", roles: [
    "Account Executive",
    "Enterprise / Strategic Account Executive",
    "Sales Development Rep (SDR)",
    "Business Development Rep (BDR)",
    "Sales Manager",
    "Sales Director",
    "Regional / Territory Sales Manager",
    "Sales Engineer / Solutions Engineer",
    "Solutions Architect / Consultant",
    "Account Manager",
    "Customer Success Manager",
    "Partnerships / Channel Manager",
    "Inside Sales Rep",
  ] },
  { label: "Marketing", roles: [
    "Chief Marketing Officer (CMO)",
    "VP of Marketing",
    "Marketing Director",
    "Demand Generation Manager",
    "Product Marketing Manager",
    "Field Marketing Manager",
    "Partner / Channel Marketing Manager",
    "Growth Marketing Manager",
  ] },
];
const ROLE_OPTIONS = ROLE_GROUPS.flatMap((g) => g.roles);
const OTHER_ROLE = "__other__";

function nameFromEmail(email) {
  const local = String(email || "").split("@")[0] || "";
  const cleaned = local.replace(/[._-]+/g, " ").replace(/\d+/g, "").trim();
  if (!cleaned) return "";
  return cleaned.split(" ").filter(Boolean).map(w => w[0].toUpperCase() + w.slice(1)).join(" ");
}
const obWait = (ms) => new Promise(r => setTimeout(r, ms));
function obExtractDomain(text) {
  const m = String(text || "").match(/(?:https?:\/\/)?(?:www\.)?([a-z0-9-]+(?:\.[a-z0-9-]+)+)/i);
  if (!m) return "";
  const d = m[1].toLowerCase();
  if (/(linkedin|twitter|x|facebook|instagram)\./.test(d)) return "";
  return d;
}

// Map a real research-stream event onto the loader's progress state.
// phase: "researching" -> "writing" -> "done"; sources = count found.
function obApplyStage(prev, ev) {
  const p = Object.assign({ phase: "researching", sources: null }, prev);
  if (ev.stage === "research" && ev.status === "start") p.phase = "researching";
  else if (ev.stage === "research" && ev.status === "done") p.sources = ev.sources;
  else if (ev.stage === "synthesis" && ev.status === "start") p.phase = "writing";
  else if (ev.stage === "synthesis" && ev.status === "done") p.phase = "writing";
  else if (ev.stage === "done") p.phase = "done";
  return p;
}

// The confirm-screen fields the interview fills, in the order the checklist shows
// them. `key` is the flat form-field name (matches the server's profileDelta keys).
const OB_CHECK_FIELDS = [
  { key: "sells", label: "What you sell" },
  { key: "icpProfile", label: "Best-fit customers" },
  { key: "icpBuyers", label: "Who you sell to" },
  { key: "pain", label: "The problem you solve" },
  { key: "differentiator", label: "What makes you different" }
];

// An honest, empty starting profile (apiOn path): company name from the domain,
// nothing invented. Contrast MP.buildCompanyProfile, which fabricates plausible
// specifics and is kept only for the offline (file://) demo.
function obEmptyProfile(name, domain) {
  return {
    company: name || "", domain: domain || "", industry: "", employees: 0, hq: "", founded: null,
    companyDoes: "", sells: "", product: "",
    icp: { profile: "", buyers: "", pain: "" },
    messaging: { valueProp: "", differentiator: "" }
  };
}

// Flatten a CompanyProfile into the 7 editable working fields the interview + form use.
function obWorkingFields(p) {
  p = p || {};
  return {
    company: p.company || "",
    companyDoes: p.companyDoes || "",
    sells: p.sells || "",
    icpProfile: (p.icp && p.icp.profile) || "",
    icpBuyers: (p.icp && p.icp.buyers) || "",
    pain: (p.icp && p.icp.pain) || "",
    differentiator: (p.messaging && p.messaging.differentiator) || ""
  };
}

// Re-inflate the working fields back into a full CompanyProfile, preserving any
// research context (industry/hq/etc.) carried on `base`.
function obComposeProfile(base, w) {
  return Object.assign({}, base, {
    company: w.company || base.company,
    companyDoes: w.companyDoes || base.companyDoes,
    sells: w.sells || base.sells,
    product: (w.sells || "").replace(/^An? /, "").split(/ that | which /)[0] || base.product,
    icp: { profile: w.icpProfile, buyers: w.icpBuyers, pain: w.pain },
    messaging: { valueProp: w.sells || base.sells, differentiator: w.differentiator }
  });
}

// Clamp working fields to the server's per-field limit (4000). Without this an
// oversized paste on the confirm screen would 400 the /interview turn and, since
// the offending text stays in state, brick every retry.
function obClampFields(w) {
  const out = {};
  Object.keys(w || {}).forEach((k) => { out[k] = String(w[k] == null ? "" : w[k]).slice(0, 4000); });
  return out;
}

// The interview's opening line — a concrete question about the first empty field,
// never a blank-page "tell me about your business".
function obFirstQuestion(company, w) {
  const co = company || "your company";
  if (!w.sells) return `Let's set up your profile with a couple of quick questions — it'll take about two minutes. In a sentence or two: what does ${co} sell?`;
  if (!w.icpProfile) return `Let's sharpen this. Who are ${co}'s best-fit customers — what kind of companies get the most value?`;
  if (!w.pain) return `Got it. What's the core problem you solve for those customers?`;
  return `Tell me a little more about ${co} and who you sell to, and I'll tighten up your profile.`;
}

// Small "the AI guessed this" marker shown next to inferred confirm-screen fields.
function InferChip() {
  return <span className="mp-infer-chip"><Icon name="Sparkles" size={11} /> inferred — worth a check</span>;
}

// ---- research card ----
// When `progress` is supplied (API path) the steps are driven by REAL stream
// events — research (grounded web search) and synthesis (selling profile) — so
// a line only completes when its backend phase actually finishes. With no
// `progress` (offline/demo) it falls back to a timed animation and calls onDone.
// The phase analysis completion lands on (issue #102). Spec qVlF9gmcp7: the
// sequence is Start -> Analyzing -> Refine -> Review, and "Analysis completion
// takes the user to Refine" -- for EVERY user, not only when research came up
// thin. Before this, a successful lookup jumped research -> profile and the
// seller never saw Refine at all.
//
// A pure function, deliberately: the caller sits inside a promise .then() in an
// effect, and web/test/render-harness.js states "Effects are deliberately NOT
// run" -- so a component-level test of this rule could only ever pass
// vacuously. Extracted so the rule itself is testable (T1/T2).
function phaseAfterResearch() { return "interview"; }

// The Review page's heading + sub (issue #102). Pure, and extracted for the
// same reason phaseAfterResearch is: computed inline in the stateful component
// this rule is unreachable from web/test/render-harness.js, which "deliberately
// does NOT run" effects -- so a future regression here would redden nothing.
// The rule is small; the cost of it being wrong is telling a seller we failed
// when we did not.
//
// WHY IT TAKES researchRich SEPARATELY FROM built: onInterviewDone() sets
// `built` to "interview" for EVERY seller once they all route through Refine,
// so `built` cannot answer "did the lookup succeed?". Keying the
// "couldn't find much online" line on it would state a failure that never
// happened. `built` still answers the different question it is good for --
// was this profile assembled from research or from the seller's own answers.
function reviewIntroCopy(built, hasSellerContent, researchRich) {
  if (built !== "interview") {
    return { title: "Here's what I found. Look right?", sub: "This is the only thing I'll ask you to confirm. Tweak anything — it tunes every brief you generate." };
  }
  if (hasSellerContent) {
    return { title: "Here's your profile. Look right?", sub: "Built from what you told me. Tweak anything — it tunes every brief you generate." };
  }
  // PROVISIONAL COPY (#102) -- awaiting Eli. Must not assert a failure that did
  // not happen: the rich branch never claims we found nothing.
  return researchRich
    ? { title: "Let's finish your profile.", sub: "Add anything we missed — every field tunes the briefs you generate." }
    : { title: "Let's finish your profile.", sub: "We couldn't find much online — fill in what you can here. Every field tunes the briefs you generate." };
}

function SetupResearch({ label, onDone, progress }) {
  const live = !!progress;
  const [done, setDone] = React.useState(0);
  React.useEffect(() => {
    if (live) return; // streaming drives the UI + completion from the parent
    let i = 0, t;
    const tick = () => { i += 1; setDone(i); if (i < 4) t = setTimeout(tick, 640); else t = setTimeout(onDone, 620); };
    t = setTimeout(tick, 520);
    return () => clearTimeout(t);
  }, [live]);

  const phase = (progress && progress.phase) || "researching";
  const sources = progress && progress.sources;
  const researchDone = phase === "writing" || phase === "done";
  const allDone = phase === "done";

  const steps = [
    { label: "Scanning " + (label || "the web"), group: "research" },
    {
      label: "Reading their site, news, funding & hiring"
        + (sources != null && sources > 0 ? "  ·  " + sources + (sources === 1 ? " source" : " sources") : ""),
      group: "research"
    },
    { label: "Mapping who they sell to & how they win", group: "synthesis" },
    { label: "Building your selling profile", group: "synthesis" }
  ];
  // In live mode, status follows real phases; in timed mode it follows `done`.
  const statusOf = (g, i) => {
    if (!live) return i < done ? "done" : i === done ? "active" : "pending";
    if (allDone) return "done";
    if (g === "research") return researchDone ? "done" : "active";
    return researchDone ? "active" : "pending";
  };

  return (
    <div className="mp-setup mp-fade">
      <div className="mp-setup__hero">
        <span className="mp-eyebrow mp-pill-eyebrow"><Icon name="Radar" size={13} /> Setting you up</span>
        <h1 className="text-heading-1">Reading up on {label || "your company"}…</h1>
        <p className="mp-setup__sub">One sec — so you never have to explain what you sell.</p>
      </div>
      <div className="mp-setup__card">
        <div className="mp-research">
          {steps.map((s, i) => {
            const st = statusOf(s.group, i);
            const isDone = st === "done", isActive = st === "active";
            return (
              <div key={i} className={`mp-research__step${st !== "pending" ? " is-on" : ""}`}>
                <span className={`mp-research__dot${isDone ? " is-done" : isActive ? " is-active" : ""}`}>
                  {isDone ? <Icon name="Check" size={15} strokeWidth={2.5} /> : isActive ? <span className="mp-spinner"></span> : <span className="mp-tinydot"></span>}
                </span>
                <span className="mp-research__label">{s.label}</span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ---- ask for company (only when we can't guess from the email) ----
function SetupAskCompany({ name, onSubmit }) {
  const [text, setText] = React.useState("");
  return (
    <div className="mp-setup mp-fade">
      <div className="mp-setup__hero">
        <span className="mp-eyebrow mp-pill-eyebrow"><Icon name="Building2" size={13} /> Quick setup</span>
        <h1 className="text-heading-1">Welcome{name ? ", " + name.split(" ")[0] : ""}.</h1>
        <p className="mp-setup__sub">What company do you sell for? Paste your website or just type the name — I'll do the digging.</p>
      </div>
      <div className="mp-bigbox" style={{ maxWidth: 560 }}>
        <textarea value={text} autoFocus rows={1} placeholder="e.g. northbeam.com — or Northbeam"
          onChange={(e) => setText(e.target.value)}
          onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); if (text.trim()) onSubmit(text.trim()); } }}
          style={{ minHeight: 0 }} />
        <div className="mp-bigbox__foot">
          <div style={{ flex: 1 }}></div>
          <Button size="lg" icon="Search" iconRight="ArrowRight" disabled={!text.trim()} onClick={() => onSubmit(text.trim())}>Look it up</Button>
        </div>
      </div>
    </div>
  );
}

// ---- guided interview (shown when research comes up thin, or via "Refine with AI") ----
// A short chat that fills the same fields the confirm screen edits. Each turn is
// stateless on the server: we send the running transcript + accumulated working
// fields and merge back the delta it learned. Ends by handing a full profile to
// the confirm screen — the seller still gets the final say there.
function SetupInterview({ company, seedProfile, onDone, researchRich }) {
  const [w, setW] = React.useState(() => obWorkingFields(seedProfile));
  const [msgs, setMsgs] = React.useState(() => [{ role: "assistant", text: obFirstQuestion(company, obWorkingFields(seedProfile)) }]);
  const [input, setInput] = React.useState("");
  const [sending, setSending] = React.useState(false);
  const [wrapUp, setWrapUp] = React.useState(false);
  const scrollRef = React.useRef(null);

  React.useEffect(() => { MP.onboardingEvent("onboarding_interview_started"); }, []);
  React.useEffect(() => {
    const el = scrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [msgs, sending]);

  const filledCount = OB_CHECK_FIELDS.filter((c) => (w[c.key] || "").trim()).length;
  const coreReady = ["sells", "icpProfile", "pain"].every((k) => (w[k] || "").trim());
  const canFinish = wrapUp || coreReady;

  const send = async () => {
    // Clamp to the server's transcript-item limit (2000) so an oversized paste
    // can't 400 the turn and then re-fail on every retry (the message persists
    // in `msgs`). The <textarea maxLength> is the first guard; this is the belt.
    const text = input.trim().slice(0, 2000);
    if (!text || sending) return;
    const nextMsgs = msgs.concat([{ role: "user", text }]);
    setMsgs(nextMsgs);
    setInput("");
    setSending(true);
    try {
      const r = await MP.interviewTurn({ company, profile: obClampFields(w), transcript: nextMsgs });
      const delta = (r && r.profileDelta) || {};
      // Merge only the non-empty fields the model learned this turn.
      setW((prev) => {
        const next = Object.assign({}, prev);
        Object.keys(delta).forEach((k) => { if (typeof delta[k] === "string" && delta[k].trim()) next[k] = delta[k].trim().slice(0, 4000); });
        return next;
      });
      setMsgs((m) => m.concat([{ role: "assistant", text: (String((r && r.reply) || "Got it.")).slice(0, 2000) }]));
      if (r && r.done) setWrapUp(true);
    } catch (e) {
      const msg = (e && e.data && e.data.message) || "I had trouble there — mind trying that again? You can also skip and fill it in on the next screen.";
      setMsgs((m) => m.concat([{ role: "assistant", text: msg }]));
    } finally {
      setSending(false);
    }
  };

  const finish = (complete) => {
    MP.onboardingEvent(complete ? "onboarding_interview_completed" : "onboarding_interview_abandoned");
    onDone(obComposeProfile(seedProfile, w));
  };

  return (
    <div className="mp-setup mp-setup--wide mp-fade">
      <div className="mp-setup__hero">
        <span className="mp-eyebrow mp-pill-eyebrow"><Icon name="MessagesSquare" size={13} /> Let's build your profile</span>
        <h1 className="text-heading-1">Tell me about {company || "your company"}.</h1>
        {/* PROVISIONAL COPY (#102) -- awaiting Eli. The rich-research wording
            below must NOT assert that research failed, because after this
            issue's routing change EVERY seller reaches Refine, including those
            whose lookup worked. Even provisional copy tells the truth. */}
        <p className="mp-setup__sub">{researchRich
          ? "Here's what I found. Anything you add now makes your briefs sharper."
          : "We couldn't find much online — no problem, plenty of great companies are quiet. A couple of quick questions and your briefs will be sharper than any website could make them."}</p>
      </div>

      <div className="mp-interview">
        <div className="mp-chat">
          <div className="mp-chat__msgs" ref={scrollRef}>
            {msgs.map((m, i) => (
              <div key={i} className={`mp-msg mp-msg--${m.role}`}>
                {m.role === "assistant" && <span className="mp-msg__ava"><Icon name="Sparkles" size={14} /></span>}
                <div className="mp-msg__bubble">{m.text}</div>
              </div>
            ))}
            {sending && (
              <div className="mp-msg mp-msg--assistant">
                <span className="mp-msg__ava"><Icon name="Sparkles" size={14} /></span>
                <div className="mp-msg__bubble mp-msg__bubble--typing"><span></span><span></span><span></span></div>
              </div>
            )}
          </div>
          <div className="mp-chat__composer">
            <textarea
              value={input}
              autoFocus
              rows={1}
              maxLength={2000}
              placeholder="Type your answer…"
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }}
            />
            <Button size="md" variant="primary" icon="ArrowUp" aria-label="Send" disabled={!input.trim() || sending} onClick={send} className="mp-chat__send" />
          </div>
        </div>

        <aside className="mp-checklist">
          <div className="mp-checklist__head">Your profile <span>{filledCount}/{OB_CHECK_FIELDS.length}</span></div>
          {OB_CHECK_FIELDS.map((c) => {
            const on = !!(w[c.key] || "").trim();
            return (
              <div key={c.key} className={`mp-checklist__item${on ? " is-on" : ""}`}>
                <span className="mp-checklist__dot">{on ? <Icon name="Check" size={13} strokeWidth={2.5} /> : <span className="mp-tinydot"></span>}</span>
                {c.label}
              </div>
            );
          })}
          <div className="mp-checklist__actions">
            <Button size="md" variant="primary" block icon="CircleCheck" iconRight="ArrowRight" disabled={!canFinish} onClick={() => finish(true)}>
              Review &amp; finish
            </Button>
            <button className="mp-linklike" onClick={() => finish(coreReady)}>I'll fill it in myself</button>
          </div>
        </aside>
      </div>
    </div>
  );
}

// ---- the ONE confirm: your selling profile (incl. ICP) ----
function SetupProfile({ profile, guessName, initialWho, evidence, intro, onRefine, onFinish }) {
  const [f, setF] = React.useState({
    name: (initialWho && initialWho.name) || guessName || "",
    role: (initialWho && initialWho.role) || "Account Executive",
    company: profile.company || "",
    companyDoes: profile.companyDoes || "",
    sells: profile.sells || "",
    icpProfile: (profile.icp && profile.icp.profile) || "",
    icpBuyers: (profile.icp && profile.icp.buyers) || "",
    pain: (profile.icp && profile.icp.pain) || "",
    differentiator: (profile.messaging && profile.messaging.differentiator) || ""
  });
  const set = (k) => (e) => setF(s => ({ ...s, [k]: e.target.value }));
  const inferred = (k) => !!(evidence && evidence[k] === "inferred" && (f[k] || "").trim());
  const currentProfile = () => Object.assign({}, profile, {
    company: f.company, companyDoes: f.companyDoes, sells: f.sells,
    product: (f.sells || "").replace(/^An? /, "").split(/ that | which /)[0] || profile.product,
    icp: { profile: f.icpProfile, buyers: f.icpBuyers, pain: f.pain },
    messaging: { valueProp: f.sells, differentiator: f.differentiator }
  });

  const [saving, setSaving] = React.useState(false);
  const finish = async () => {
    const prof = currentProfile();
    if (MP.apiOn) {
      setSaving(true);
      try {
        await MP.completeOnboarding({ name: f.name, role: f.role, profile: prof });
        onFinish();
        return;
      } catch (e) {
        setSaving(false);
      }
    }
    MP.setUser({ name: f.name, role: f.role, company: prof.company, companyDoes: prof.companyDoes, sells: prof.sells, domain: prof.domain, profile: prof });
    onFinish();
  };

  return (
    <div className="mp-setup mp-setup--wide mp-fade">
      <div className="mp-setup__hero">
        <span className="mp-eyebrow mp-pill-eyebrow is-green"><Icon name="CircleCheck" size={13} /> Almost done</span>
        <h1 className="text-heading-1">{(intro && intro.title) || "Here's what I found. Look right?"}</h1>
        <p className="mp-setup__sub">{(intro && intro.sub) || "This is the only thing I'll ask you to confirm. Tweak anything — it tunes every brief you generate."}</p>
      </div>

      <div className="mp-profileform">
        {/* context the research pulled — read-only, just to show it did the work */}
        <div className="mp-profileform__context">
          <Avatar name={f.company || "?"} color="blue" size={38} soft />
          <div className="mp-pf-ctx__meta">
            <div className="mp-pf-ctx__co">{f.company || "Your company"}</div>
            <div className="mp-pf-ctx__chips">
              {/* ternaries, not `x && <…>` — employees is 0 on an honest-empty
                  profile and `{0 && …}` would render a stray "0". */}
              {profile.industry ? <span><Icon name="Building2" size={12} /> {profile.industry}</span> : null}
              {profile.employees > 0 ? <span style={{ fontFamily: "var(--font-numbers)" }}><Icon name="Users" size={12} /> ~{profile.employees.toLocaleString()}</span> : null}
              {profile.hq ? <span><Icon name="MapPin" size={12} /> {profile.hq}</span> : null}
            </div>
          </div>
        </div>

        <div className="mp-pf-group">
          <div className="mp-pf-group__head"><Icon name="User" size={15} /> You</div>
          <div className="mp-grid-2">
            <Field label="Your name"><Input value={f.name} onChange={set("name")} placeholder="Jordan Rivera" /></Field>
            <Field label="Your role">
              {(() => {
                const isOther = !ROLE_OPTIONS.includes(f.role); // "" or a custom title
                return (
                  <>
                    <Select
                      value={isOther ? OTHER_ROLE : f.role}
                      onChange={(e) => { const v = e.target.value; setF(s => ({ ...s, role: v === OTHER_ROLE ? "" : v })); }}
                    >
                      {ROLE_GROUPS.map(g => (
                        <optgroup key={g.label} label={g.label}>
                          {g.roles.map(r => <option key={r} value={r}>{r}</option>)}
                        </optgroup>
                      ))}
                      <option value={OTHER_ROLE}>Other…</option>
                    </Select>
                    {isOther && (
                      <Input
                        style={{ marginTop: 8 }}
                        value={f.role}
                        onChange={set("role")}
                        placeholder="Type your role"
                        aria-label="Your role"
                        autoFocus
                      />
                    )}
                  </>
                );
              })()}
            </Field>
          </div>
        </div>

        <div className="mp-pf-group">
          <div className="mp-pf-group__head"><Icon name="Briefcase" size={15} /> What you sell</div>
          <Field label={<>What does your company do? {inferred("companyDoes") && <InferChip />}</>}><Textarea rows={4} value={f.companyDoes} onChange={set("companyDoes")} /></Field>
          <Field label={<>What do you sell? {inferred("sells") && <InferChip />}</>}><Textarea rows={4} value={f.sells} onChange={set("sells")} /></Field>
        </div>

        <div className="mp-pf-group mp-pf-group--accent">
          <div className="mp-pf-group__head"><Icon name="Target" size={15} /> Who you sell to <span className="mp-pf-group__why">— this is how I tell if a prospect is a good fit</span></div>
          <div className="mp-grid-2">
            <Field label={<>Best-fit customers {inferred("icpProfile") && <InferChip />}</>}><Textarea rows={4} value={f.icpProfile} onChange={set("icpProfile")} placeholder="Mid-market teams, 200–2,000 people" /></Field>
            <Field label={<>Who you sell to {inferred("icpBuyers") && <InferChip />}</>}><Textarea rows={4} value={f.icpBuyers} onChange={set("icpBuyers")} placeholder="VP Sales, RevOps" /></Field>
          </div>
          <Field label={<>The problem you solve for them {inferred("pain") && <InferChip />}</>}><Textarea rows={4} value={f.pain} onChange={set("pain")} placeholder="manual busywork slowing their team down" /></Field>
          <Field label={<>What makes you different {inferred("differentiator") && <InferChip />}</>}><Textarea rows={4} value={f.differentiator} onChange={set("differentiator")} /></Field>
        </div>

        <div className="mp-create__actions" style={{ marginTop: 4 }}>
          <span className="mp-setup__editnote">
            {onRefine
              ? <button type="button" className="mp-linklike" onClick={() => onRefine(currentProfile(), { name: f.name, role: f.role })}><Icon name="Sparkles" size={14} /> Not quite right? Refine with AI</button>
              : <><Icon name="Wand2" size={14} /> You can change any of this later in Settings.</>}
          </span>
          <Button size="lg" icon="LayoutDashboard" iconRight="ArrowRight" disabled={!f.company.trim() || saving} onClick={finish}>{saving ? "Saving…" : "Start prepping"}</Button>
        </div>
      </div>
    </div>
  );
}

function OnboardingPage() {
  const email = MP.get().pendingEmail || "";
  const guessName = nameFromEmail(email);
  const dom = MP.domainOf(email);
  const business = MP.isBusinessDomain(email);

  // phase: ask | research | interview | profile
  const [phase, setPhase] = React.useState(business ? "research" : "ask");
  const [label, setLabel] = React.useState(business ? dom : "");
  // Live research progress (API path). null ⇒ offline/demo timed fallback.
  const [progress, setProgress] = React.useState(null);
  const [profile, setProfile] = React.useState(business
    ? (MP.apiOn ? obEmptyProfile(MP.companyNameFromDomain(dom), dom) : MP.buildCompanyProfile(MP.companyNameFromDomain(dom), dom))
    : obEmptyProfile("", ""));
  const [evidence, setEvidence] = React.useState(null);
  // "Your name" on Review starts as the name on the user's shared Evolve account (what /api/me reports, the
  // one Settings shows). The email guess is empty for a Suite sign-in: nothing sets `pendingEmail` any more.
  const accountName = (MP.get().user && MP.get().user.name) || "";
  const [who, setWho] = React.useState({ name: accountName || guessName || "", role: "Account Executive" });
  // "research" ⇒ we found the company; "interview" ⇒ the seller told us (thin
  // research or Refine-with-AI). Drives the confirm-screen header honesty.
  const [built, setBuilt] = React.useState("research");
  // Did the company lookup actually FIND something? (issue #102)
  //
  // Separate from `built` on purpose. `built` records how the profile was
  // assembled and onInterviewDone() sets it to "interview" for EVERY user who
  // passes through Refine -- which, after this issue's routing change, is
  // everyone. So `built` can no longer answer "did research succeed?", and the
  // copy that claims we found nothing must key off THIS instead. Without it a
  // seller whose lookup worked, who then adds nothing in Refine, would be told
  // "We couldn't find much online" about a company we researched fine.
  const [researchRich, setResearchRich] = React.useState(false);
  // Bumped whenever `profile` is swapped under a mounted SetupProfile (thin→
  // interview→confirm, or Refine) to force its form state to re-initialize.
  const [rev, setRev] = React.useState(0);

  const startResearch = (nameOrDomain, d) => {
    // apiOn seeds an HONEST empty profile (name from domain, nothing invented);
    // the offline demo still uses the fabricated generator for believable data.
    const seed = MP.apiOn
      ? obEmptyProfile(MP.companyNameFromDomain(d) || nameOrDomain || "Your company", d)
      : MP.buildCompanyProfile(MP.companyNameFromDomain(d) || nameOrDomain || "Your company", d);
    setProfile(seed);
    if (!MP.apiOn) return; // offline: timed loader advances via onResearchDone
    // Real streaming: each phase updates the loader; the stream's completion
    // (not a timer) decides where we land — the confirm card, or the interview
    // when research came up too thin to present as fact.
    setProgress({ phase: "researching", sources: null });
    MP.researchCompanyRemoteStream(nameOrDomain, d, (ev) => setProgress((p) => obApplyStage(p, ev)))
      .then((r) => {
        if (r && r.profile) setProfile(r.profile);
        setEvidence((r && r.evidence) || null);
        const thin = !!(r && r.thin);
        setBuilt(thin ? "interview" : "research");
        setResearchRich(!thin);
        setPhase(phaseAfterResearch());
      })
      .catch(() => {
        // Hard failure: keep the honest-empty seed and guide the seller through
        // the interview rather than presenting invented "findings".
        setEvidence(null);
        setBuilt("interview");
        setResearchRich(false);
        setPhase("interview");
      });
  };

  React.useEffect(() => {
    if (business) startResearch(dom, dom);
  }, []);

  const onCompany = (text) => {
    const d = obExtractDomain(text);
    const company = d ? MP.companyNameFromDomain(d) : text.replace(/^https?:\/\//, "").trim();
    setLabel(d || company || "your company");
    setPhase("research");
    startResearch(d || company || text, d);
  };

  const companyName = (profile && profile.company) || MP.companyNameFromDomain(dom) || label || "your company";

  // Interview handed us a finished profile → confirm screen (built by "you").
  const onInterviewDone = (builtProfile) => {
    setProfile(builtProfile);
    // The profile is now the seller's own work — clear the research groundedness
    // hints so their typed answers aren't mislabeled "inferred" under a header
    // that says "Built from what you told me".
    setEvidence(null);
    setRev((v) => v + 1);
    setBuilt("interview");
    setPhase("profile");
  };
  // "Refine with AI" from the confirm screen → interview, seeded with edits.
  const onRefine = (current, whoNow) => {
    setProfile(current);
    if (whoNow) setWho(whoNow);
    setRev((v) => v + 1);
    MP.onboardingEvent("onboarding_refine_opened");
    setPhase("interview");
  };

  // Only claim "built from what you told me" when the seller actually supplied
  // something; an immediate skip lands on an honestly-empty form with honest copy.
  const hasSellerContent = ["companyDoes", "sells"].some((k) => profile && (profile[k] || "").trim())
    || !!(profile && profile.icp && ((profile.icp.profile || "").trim() || (profile.icp.pain || "").trim()));
  const intro = reviewIntroCopy(built, hasSellerContent, researchRich);

  // No identity header in the body: the Suite shell beside the page already
  // names the product (Eli 2026-10-05; spec qVlF9gmcp7 rev 13; Figma 41:12019).
  return (
    <div className="mp-onb mp-fade">
      <div className="mp-onb__scroll">
        {phase === "ask" && <SetupAskCompany name={guessName} onSubmit={onCompany} />}
        {phase === "research" && <SetupResearch label={label} onDone={() => setPhase(phaseAfterResearch())} progress={progress} />}
        {phase === "interview" && <SetupInterview company={companyName} seedProfile={profile} onDone={onInterviewDone} researchRich={researchRich} />}
        {phase === "profile" && (
          <SetupProfile
            key={rev}
            profile={profile}
            guessName={guessName}
            initialWho={who}
            evidence={evidence}
            intro={intro}
            onRefine={onRefine}
            onFinish={() => navigate("/dashboard")}
          />
        )}
      </div>
    </div>
  );
}

// phaseAfterResearch / SetupInterview are exported for
// web/test/onboarding-header-and-flow.test.js (#102/#103). The routing rule and
// the copy gating are otherwise unreachable from a test: they sit inside an
// effect the shallow harness deliberately does not run.
Object.assign(window, { OnboardingPage, ROLE_OPTIONS, ROLE_GROUPS, phaseAfterResearch, reviewIntroCopy, SetupInterview });
