/* global React, ReactDOM, window */
/* Sales demo — the Sales Flow home/designing/intake screens running live inside
   one product shell, wired through to studies and results. */
const { useState: sfS, useEffect: sfE } = React;

const SF_NAV = [
  { id: "home", icon: "home", label: "Home" },
  { id: "studies", icon: "clipboard-list", label: "Studies" },
  { id: "assets", icon: "shirt", label: "Assets" },
  { id: "library", icon: "books", label: "Library" }
];

const SF_TIERS = {
  quick: { label: "Gut check", meta: "~2 hours · fastest read", sample: "300 respondents · UK, FR, DE", audience: "300 respondents", lands: "today" },
  standard: { label: "Decision support", meta: "2–3 days · balanced read", sample: "1,240 respondents · UK, FR, DE", audience: "1,240 respondents", lands: "Friday" },
  extended: { label: "Defensible research", meta: "10–14 days · full consumer panel", sample: "2,000 respondents · UK, FR, DE, US", audience: "2,000 respondents", lands: "in about two weeks" }
};

const SEED = [{
  id: "demo-study",
  name: "Women's Apparel — FW26 range read",
  owner: "Dan Leahy",
  updated: "Today · 2 hrs ago",
  mins: 120,
  status: "Live",
  responses: 1240,
  profiles: 3,
  badge: "mint",
  cas: ["ca-age", "ca-gender", "ca-activity"]
}];

const SF_TWEAKS = /*EDITMODE-BEGIN*/{
  "designSeconds": 9,
  "publishSeconds": 1.2
}/*EDITMODE-END*/;

function SFResults() {
  if (!window.ApparelInsights) return <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100%", color: "#636c79", fontSize: 13 }}>Loading results…</div>;
  return (
    <div className="gl-page od-embed" style={{ flex: 1, minHeight: 0 }}>
      <div className="pf-app"><div className="gl-appbody">
        <window.ApparelInsights viewMode="client" claimDivider={false} confMode="badge" railMode="none" confAlways />
      </div></div>
    </div>
  );
}

/* The publish confirmation was a 492px modal, then a full "review" route
   (window.StudyCheckout). Both are gone: the builder's own Audience and timing
   answers are the decision, and re-asking them behind a gate turned a decision
   into a form. Submitting goes straight to SFLanded. StudyCheckout still ships
   as its own page ("Study Checkout.html"); it is just not in this flow. */

function SFLanded({ tier, name, opt, onGo }) {
  const t = SF_TIERS[tier] || SF_TIERS.standard;
  /* `opt` is an explicit audience confirmation, and it — not the tier — is the
     last word on what got sent when one exists. Nothing supplies one in this
     flow, so the tier the builder resolved to carries the copy. */
  const steps = [
    /* opt.name is used as written, not lower-cased — it is a name, not a
       sentence fragment. */
    { k: "live", icon: "broadcast", label: "In field now",
      val: opt ? opt.total.toLocaleString() + " respondents · " + opt.name : t.sample },
    { k: "read", icon: "wave-sine", label: "Surveying", val: opt ? opt.back + " · " + opt.who : t.meta },
    { k: "land", icon: "mail", label: "Delivery", val: "We'll email you when the results are in!" }
  ];
  return (
    <div className="sf-land">
      <div className="sf-land__motes">{Array.from({ length: 9 }).map((_, i) => <span key={i} style={{ "--i": i }}></span>)}</div>
      <div className="sf-land__card">
        <div className="sf-land__seal">
          <svg viewBox="0 0 44 44" aria-hidden="true"><circle className="sf-land__ring" cx="22" cy="22" r="19" /><path className="sf-land__tick" d="M13.5 22.8l5.6 5.4L31 16.4" /></svg>
        </div>
        <div className="sf-land__kicker">It's out the door</div>
        <h2 className="sf-land__h">{name}</h2>
        <div className="sf-land__sub">Nothing left to chase. We'll take it from here and bring back three pages you can put in front of the room.</div>
        <div className="sf-land__steps">
          <span className="sf-land__thread"></span>
          {steps.map((s, i) => (
            <div className="sf-land__step" key={s.k} style={{ "--d": (0.55 + i * 0.16) + "s" }}>
              <span className="sf-land__dot"><i className={"ti ti-" + s.icon}></i></span>
              <b>{s.label}</b><span>{s.val}</span>
            </div>
          ))}
        </div>
        <button className="sf-land__go" onClick={onGo}>See early data<i className="ti ti-arrow-right"></i></button>
      </div>
    </div>
  );
}

/* ---------- shareable links ----------
   The demo is one page, so every link to it used to land on Home and whoever
   you sent it to had to be walked through the clicks to reach what you meant.
   Each screen now writes itself into the address bar, reads one back on load,
   and follows Back/Forward, so the address bar is always the link to hand
   over — copy it out of the browser the way you would any other page.

     #/                      home
     #/studies               the studies list
     #/studies/<id>          that study, on Insights
     #/studies/<id>/<tab>    that study, on intake | insights | results | report
     #/assets                the asset library
     #/library               the question library
     #/new                   the manual study builder

   Hash, not History: these builds are static files behind a single rewrite for
   "/", so /studies/sv-gsc would 404 on a hard refresh. The two in-flight
   screens — designing and review — are deliberately unaddressable: they only
   exist part-way through a submission, and a link that dropped you into one
   would restore the chrome without the answers behind it. */
const SF_ROUTE_PATH = { home: "", studies: "studies", assets: "assets", library: "library", intake: "new" };
const SF_PATH_ROUTE = { "": "home", studies: "studies", assets: "assets", library: "library", "new": "intake" };
const SF_TAB_IDS = ["intake", "insights", "results", "report"];

function sfNormHash(h) {
  return String(h || "").replace(/^#/, "").replace(/^\/+/, "").replace(/\/+$/, "");
}

function sfParseHash(h) {
  const parts = sfNormHash(h).split("?")[0].split("/").filter(Boolean).map(decodeURIComponent);
  if (!parts.length) return { route: "home" };
  if (parts[0] === "studies" && parts[1]) {
    return { route: "study", studyId: parts[1], tab: SF_TAB_IDS.indexOf(parts[2]) >= 0 ? parts[2] : "insights" };
  }
  const r = SF_PATH_ROUTE[parts[0]];
  return { route: r || "home" };
}

/* null means "this screen has no address" — leave whatever is in the bar alone. */
function sfFormatHash(route, study, tab, manual) {
  if (route === "study") return study ? "#/studies/" + encodeURIComponent(study.id) + (tab && tab !== "insights" ? "/" + tab : "") : null;
  if (route === "intake") return manual ? "#/new" : null;
  const p = SF_ROUTE_PATH[route];
  return p == null ? null : (p ? "#/" + p : "#/");
}

/* Studies a link is allowed to name: the seeded rows plus anything published in
   this session. A session study's id is a timestamp, so its link only resolves
   for the person who made it — an unknown id falls back to the list rather than
   to an empty page. */
function sfFindStudy(id, session) {
  const all = [].concat(window.SEED_STUDIES || [], SEED, session || []);
  for (let i = 0; i < all.length; i++) if (all[i] && all[i].id === id) return all[i];
  return null;
}

function SFApp() {
  const [tweaks, setTweak] = window.useTweaks(SF_TWEAKS);
  const [route, setRoute] = sfS("home");
  const [brief, setBrief] = sfS("");
  const [briefFiles, setBriefFiles] = sfS([]);
  const [moment, setMoment] = sfS(null);
  const [tier, setTier] = sfS("standard");
  /* The intake screen owns the decision context. It is lifted here on the way
     through so anything downstream reads the same answers the intake rail did,
     rather than recomputing them. `dcAns` is the four raw chat answers. */
  const [dc, setDc] = sfS(null);
  const [dcAns, setDcAns] = sfS(null);
  const [busy, setBusy] = sfS(false);
  const [toast, setToast] = sfS(null);
  const [studies, setStudies] = sfS(SEED);
  const [openStudy, setOpenStudy] = sfS(null);
  /* Which tab of the study page is showing. It lives here, not in the page,
     because the address bar names it and a link can arrive already set. */
  const [studyTab, setStudyTab] = sfS("insights");
  const [homeKey, setHomeKey] = sfS(0);
  const [listKey, setListKey] = sfS(0);
  const [manual, setManual] = sfS(false);
  const [landed, setLanded] = sfS(null);
  /* The name follows the moment picked on home, so a Line Adoption demo does
     not open a study called "key account sell-in". It stays editable in the
     crumb; starting a new study is what resets it. */
  const [studyName, setStudyName] = sfS(window.EH.studyFor(null));

  /* The designing beat is a loading state, not a decision point: it advances
     on its timer into the intake screen, where the decision-context questions
     are docked above the composer. */
  sfE(() => {
    if (route !== "designing") return;
    const id = setTimeout(() => setRoute("intake"), Math.max(1, tweaks.designSeconds) * 1000);
    return () => clearTimeout(id);
  }, [route, tweaks.designSeconds]);

  sfE(() => {
    if (!toast) return;
    const id = setTimeout(() => setToast(null), 4500);
    return () => clearTimeout(id);
  }, [toast]);

  /* `tr` only arrives from the Submit Intake modal, where the merchant answered
     "when do you need the results" outright. It seeds the tier so the study
     publishes on the method that answer implies, exactly as a same-day answer
     in the intake dock does. A composer submission leaves it alone. */
  const start = ({ brief: b, files, moment: mo, tier: tr }) => { setBrief(b); setBriefFiles(files || []); setDc(null); setDcAns(null); setMoment(mo || null); if (tr && SF_TIERS[tr]) setTier(tr); setStudyName(window.EH.studyFor(mo || null)); setManual(false); setRoute("designing"); };
  const goManual = () => { setBrief(""); setBriefFiles([]); setMoment(null); setStudyName(window.EH.studyFor(null)); setManual(true); setRoute("intake"); };
  const goHome = () => { setHomeKey(k => k + 1); setBrief(""); setBriefFiles([]); setMoment(null); setStudyName(window.EH.studyFor(null)); setManual(false); setOpenStudy(null); setRoute("home"); };
  const nav = (id) => {
    if (id === "home") return goHome();
    setOpenStudy(null);
    setRoute(id);
  };

  /* `tierNow` is passed in rather than read from state: the caller sets the tier
     and publishes in the same handler, and React has not flushed the setter by
     then, so the closure still holds the previous tier. `opt` is an audience
     option a review screen would have confirmed; nothing supplies one now, and
     the confirmation falls back to the tier copy, which is the same answer. */
  const confirmPublish = (opt, tierNow) => {
    const useTier = tierNow || tier;
    setBusy(true);
    setTimeout(() => {
      const s = {
        id: "sv-" + Date.now(),
        name: studyName,
        owner: "Dan Leahy",
        updated: "Today · just now",
        mins: -1,
        status: "Live",
        responses: 0,
        profiles: 3,
        badge: "mint",
        cas: ["ca-age", "ca-gender", "ca-activity"]
      };
      setStudies(p => [s, ...p]);
      setListKey(k => k + 1);
      setBusy(false);
      setBrief("");
      setRoute("studies");
      setLanded({ tier: useTier, name: s.name, opt: opt || null });
    }, Math.max(0, tweaks.publishSeconds) * 1000);
  };

  const navActive = route === "home" ? "home"
    : (route === "designing" || route === "intake" || route === "studies" || route === "study") ? "studies"
    : route;

  const crumb = route === "designing" ? <React.Fragment><span className="sl">/</span><span>New study · designing</span></React.Fragment>
    : route === "intake" ? <React.Fragment><span className="sl">/</span><button onClick={() => nav("studies")}>Studies</button><span className="sl">/</span><input className="sf-crumb__edit" value={studyName} onChange={e => setStudyName(e.target.value)} onKeyDown={e => { if (e.key === "Enter") e.target.blur(); }} size={Math.max(12, studyName.length)} aria-label="Study name" /></React.Fragment>
    : route === "study" ? <React.Fragment><span className="sl">/</span><button onClick={() => { setOpenStudy(null); setRoute("studies"); }}><i className="ti ti-chevron-left"></i>All studies</button><span className="sl">/</span><b>{openStudy && openStudy.name}</b><i className="ti ti-circle-check-filled sf-crumb__status" title="Closed study" aria-label="Closed study"></i></React.Fragment>
    : null;

  const clickableIds = ["demo-study", "sv-gola", "sv-apparel", "sv-gsc", ...studies.map(s => s.id)];

  /* Which respondent file the study surfaces read. Set before the study page
     mounts (and the page is keyed on the study id) so Raw Results, the
     sentiment benchmarks and the product imagery all resolve against the same
     study rather than whichever one happened to load first. */
  const openStudyPage = (st, tab) => {
    const cfg = window.srpStudyCfg ? window.srpStudyCfg(st) : null;
    if (cfg) {
      window.ACTIVE_STUDY_JSON = cfg.json;
      if (cfg.cfg) window.ACTIVE_STUDY_CFG = cfg.cfg;
      else if (window.__BASE_STUDY_CFG) window.ACTIVE_STUDY_CFG = window.__BASE_STUDY_CFG;
      const prof = cfg.profiles && cfg.profiles();
      window.ACTIVE_PROFILES = prof || window.__BASE_PROFILES || [];
    }
    setStudyTab(SF_TAB_IDS.indexOf(tab) >= 0 ? tab : "insights");
    setOpenStudy(st);
    setRoute("study");
  };

  /* Read the URL on load and whenever the user moves through history. Kept in a
     ref so the listener is registered once and still sees the current studies. */
  const sfApply = React.useRef(null);
  sfApply.current = () => {
    const t = sfParseHash(window.location.hash);
    if (t.route === "study") {
      const st = sfFindStudy(t.studyId, studies);
      if (st) return openStudyPage(st, t.tab);
      setOpenStudy(null);
      return setRoute("studies");
    }
    if (t.route === "intake") return goManual();
    setOpenStudy(null);
    setRoute(t.route);
  };
  sfE(() => {
    if (sfNormHash(window.location.hash)) sfApply.current();
    const on = () => sfApply.current();
    window.addEventListener("hashchange", on);
    return () => window.removeEventListener("hashchange", on);
  }, []);

  /* Write the URL back. Nothing to do when it already says what we mean, which
     is what keeps Back from bouncing straight forward again. The first write of
     the session replaces rather than pushes, so one Back still leaves the demo. */
  const sfWrote = React.useRef(false);
  sfE(() => {
    const h = sfFormatHash(route, openStudy, studyTab, manual);
    if (h == null || sfNormHash(h) === sfNormHash(window.location.hash)) return;
    if (sfWrote.current) window.history.pushState(null, "", h);
    else window.history.replaceState(null, "", h);
    sfWrote.current = true;
  }, [route, openStudy, studyTab, manual]);

  return (
    <React.Fragment>
      <div className="sf-app">
        <div className="sf-top">
          <span className="sf-top__logo"><img className="brand-mark" src="brand/tecovas-mark.png" alt="Tecovas" /></span>
          {crumb && <span className="sf-crumb">{crumb}</span>}
          <span className="sf-top__sp"></span>
          <span className="sf-top__user">DL</span>
        </div>
        <div className="sf-body">
          <div className="sf-nav">
            {SF_NAV.map(x => (
              <button key={x.id} className={"sf-nav__b" + (x.id === navActive ? " on" : "")} onClick={() => nav(x.id)}>
                <i className={"ti ti-" + x.icon}></i><em>{x.label}</em>
              </button>
            ))}
          </div>
          <div className="sf-page">
            {route === "home" ? (
              <window.EHHome key={homeKey} bare onStart={start} onBuildManual={goManual} />
            ) : route === "designing" ? (
              <window.IntakeLoading running bare brief={brief} onSkip={() => setRoute("intake")} />
            ) : route === "intake" ? (
              /* Submitting the study goes straight to the confirmation. The
                 checkout that used to sit here made the builder answer the
                 audience and timeline question a second time, after the
                 Audience tab had already answered it — a review gate that
                 re-asks what you just told it reads as a form, not a decision.
                 The tier the builder resolved to is the last word on what got
                 sent, and the confirmation says so. */
              <div style={{ display: "flex", flex: 1, minHeight: 0 }}>
                <window.IntakeScreen bare brief={brief} briefFiles={briefFiles} manual={manual} moment={moment} studyName={studyName} initialTab={manual ? "qs" : "what"}
                  onPublish={(t, d, a) => { setTier(t); setDc(d || null); setDcAns(a || null); confirmPublish(null, t); }} />
              </div>
            ) : route === "studies" ? (
              <window.StudiesList
                key={listKey}
                extraSurveys={studies}
                justPublishedId={null}
                clickableIds={clickableIds}
                onOpenStudy={openStudyPage}
                onNewStudy={goHome}
              />
            ) : route === "study" ? (
              <window.SFStudyPage key={openStudy ? openStudy.id : "none"} study={openStudy} tab={studyTab} onTab={setStudyTab} />
            ) : route === "assets" ? (
              <window.AssetsPage onNewStudy={goHome} />
            ) : route === "library" ? (
              <window.LibraryPage />
            ) : null}
          </div>
        </div>
      </div>
      {landed && <SFLanded tier={landed.tier} name={landed.name} opt={landed.opt} onGo={() => { setLanded(null); setRoute("studies"); }} />}
      {toast && <div className="sf-toast"><i className="ti ti-circle-check-filled"></i>{toast}</div>}
      <window.TweaksPanel title="Sales demo">
        <window.TweakSection label="Pacing" />
        <window.TweakSlider label="Designing" value={tweaks.designSeconds} min={2} max={20} step={1} unit="s" onChange={v => setTweak("designSeconds", v)} />
        <window.TweakSlider label="Publishing" value={tweaks.publishSeconds} min={0} max={4} step={0.2} unit="s" onChange={v => setTweak("publishSeconds", v)} />
        <window.TweakSection label="Jump to" />
        <window.TweakRadio label="Screen" value={route === "study" ? "studies" : route} options={["home", "designing", "intake", "studies"]} onChange={setRoute} />
      </window.TweaksPanel>
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<SFApp />);
