/* /quoteflow — PRIVATE demonstration page (WO-2026-07-13-quoteflow-demo).
 *
 * UNLISTED BY DESIGN: this page is reachable only by deep link
 * (…/#/quoteflow?k=<access-key>). It is not referenced from the nav, the
 * footer, the homepage, the sitemap, or any other page — keep it that way.
 * The access key rides the hash query and is enforced SERVER-SIDE by
 * /api/demo-quote; the page itself only decides which empty-state to show.
 *
 * Copy lives in site/content.js (window.solasContent.quoteflow) — CMO pass
 * is S5 of the work order. Layout reuses the site's quote-form primitives
 * (kit.css) so the Head of Design pass (S4) works the system, not against it.
 *
 * The wire shape from /api/demo-quote is identical in fixture and live
 * modes, so arming the live rater (Phases B/C) changes nothing here.
 */

const CQ = window.solasContent.quoteflow;

const readAccessKey = () => {
  const hash = window.location.hash || "";
  const qIdx = hash.indexOf("?");
  if (qIdx === -1) return "";
  const params = new URLSearchParams(hash.slice(qIdx + 1));
  return (params.get("k") || "").trim();
};

const gbp = (v) => {
  const n = Number(v);
  if (!Number.isFinite(n)) return "£—";
  return n.toLocaleString("en-GB", { style: "currency", currency: "GBP" });
};

// Feature keys and banded levels as the calibration names them, rendered in
// plain English. Anything unmapped falls through to the raw key, which is the
// honest failure mode — a new factor shows up unlabelled rather than hidden.
const SIGNAL_LABELS = {
  flood_risk_zone: "Flood zone",
  lidar_elevation_m: "Elevation (LiDAR)",
  soil_shrink_swell: "Ground stability",
  crime_score: "Local crime index",
  epc_rating: "Energy performance (EPC)",
};

const SIGNAL_VALUES = {
  zone_1: "Zone 1 — low risk",
  zone_2: "Zone 2 — moderate",
  zone_3a: "Zone 3a — high",
  zone_3b: "Zone 3b — highest",
  under_5m: "Under 5m above sea level",
  "5_15m": "5–15m above sea level",
  "15_50m": "15–50m above sea level",
  over_50m: "Over 50m above sea level",
  low: "Below average",
  medium: "Around average",
  high: "Above average",
  moderate: "Moderate",
  unknown: "Not available",
  a: "Band A", b: "Band B", c: "Band C", d: "Band D",
  e: "Band E", f: "Band F", g: "Band G",
};

const PERIL_LABELS = {
  fire: "Fire",
  flood: "Flood",
  escape_of_water: "Escape of water",
  subsidence: "Subsidence",
  theft: "Theft",
  property_owners_liability: "Property owners' liability",
};

// Signals the rater reads off the property itself. The rating_audit feature_set
// is keyed by peril and the same feature can appear under several perils
// (flood_risk_zone feeds both flood and subsidence), so flatten and de-duplicate
// on feature name — the banded level is the same wherever it appears.
const deriveSignals = (quote) => {
  const fs = (quote.rating_audit && quote.rating_audit.feature_set) || {};
  const seen = new Map();
  Object.values(fs).forEach((features) => {
    Object.entries(features || {}).forEach(([feature, value]) => {
      if (!seen.has(feature)) seen.set(feature, { feature, value });
    });
  });
  return [...seen.values()];
};

const QuoteflowResult = ({ mode, quote }) => {
  const total = Number(quote.total_premium_incl_ipt);
  const monthly = total / 12;
  const perils = quote.peril_breakdown || [];
  const maxPeril = Math.max(...perils.map((p) => Number(p.gross_premium)), 1);
  const signals = deriveSignals(quote);
  return (
    <div aria-live="polite" className="qf-reveal">
      <header className="page-section__head" style={{ position: "static", marginBottom: 8 }}>
        <div className="page-section__eyebrow">{CQ.resultEyebrow}</div>
      </header>

      <div className="qf-premium">
        <span className="qf-premium__figure">{gbp(total)}</span>
        <span className="qf-premium__qualifier">{CQ.annualLabel}</span>
      </div>
      <div className="qf-premium__monthly">
        {gbp(monthly)} {CQ.monthlyLabel}
      </div>

      {mode === "fixture" && (
        <p className="form-response" role="status" style={{ marginTop: 14 }}>{CQ.fixtureNote}</p>
      )}

      {/* Status renders WITH the number, at body size, in full ink. The work
          order required the caveat "with the number itself, not a footnote";
          the build had it at the foot of the page. */}
      <div className="qf-status">
        <div className="qf-status__lbl">{CQ.statusLabel}</div>
        <p className="qf-status__body">{CQ.caveat}</p>
      </div>

      {/* The decorative 64px accent rule that sat here is gone — The Tenth:
          Ink-Blue marks the mechanism, never ornament. */}
      <h3 className="page-section__title" style={{ fontSize: "var(--type-h4)", margin: 0 }}>{CQ.breakdownTitle}</h3>
      <p className="argument__p" style={{ maxWidth: "58ch", marginTop: 8 }}>{CQ.breakdownNote}</p>

      <div className="qf-perils">
        {perils.map((p) => (
          <div key={p.peril_name} className="qf-peril">
            <span className="qf-peril__name">{PERIL_LABELS[p.peril_name] || p.peril_name}</span>
            <span aria-hidden="true" className="qf-peril__bar" style={{ width: `${Math.max(3, (Number(p.gross_premium) / maxPeril) * 100)}%` }} />
            <span className="qf-peril__amount">{gbp(p.gross_premium)}</span>
          </div>
        ))}
      </div>

      {/* The mechanism made visible: the signals the model actually read off
          the property, before the customer typed anything. Rendered from
          rating_audit.feature_set — bands, never raw values, so no personal
          data reaches this surface. */}
      {signals.length > 0 && (
        <React.Fragment>
          <h3 className="page-section__title" style={{ fontSize: "var(--type-h4)", margin: "36px 0 0" }}>{CQ.signalsTitle}</h3>
          <p className="argument__p" style={{ maxWidth: "58ch", marginTop: 8 }}>{CQ.signalsNote}</p>
          <dl className="qf-signals">
            {signals.map((s) => (
              <div key={s.feature} className="qf-signal">
                <dt className="qf-signal__name">{SIGNAL_LABELS[s.feature] || s.feature}</dt>
                <dd className="qf-signal__value">{SIGNAL_VALUES[s.value] || s.value}</dd>
              </div>
            ))}
          </dl>
        </React.Fragment>
      )}

      {/* §3 meta grid — clean 2×2 in the constrained aside, row hairlines */}
      <dl className="qf-meta">
        <div><dt>{CQ.grossLabel}</dt><dd>{gbp(quote.total_gross_premium)}</dd></div>
        <div><dt>{CQ.iptLabel} ({Math.round(Number(quote.ipt_rate) * 100)}%)</dt><dd>{gbp(quote.ipt_amount)}</dd></div>
        <div className="qf-meta__model"><dt>{CQ.modelLabel}</dt><dd>{quote.model_version}</dd></div>
        <div>
          <dt>{signals.length ? CQ.signalsRatedLabel : CQ.ratedLabel}</dt>
          <dd>
            {signals.length ? `${signals.length} · ` : ""}
            {new Date(quote.rated_at).toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" })}
          </dd>
        </div>
      </dl>
    </div>
  );
};

const QuoteflowPage = () => {
  // Re-read the key on every hash change: the router doesn't remount this
  // component when only the ?k= query part of the hash changes.
  const [accessKey, setAccessKey] = React.useState(readAccessKey);
  React.useEffect(() => {
    const onHash = () => setAccessKey(readAccessKey());
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);
  const [state, setState] = React.useState({ phase: "idle" }); // idle | working | done | error
  const [fields, setFields] = React.useState({
    postcode: "",
    property_type: "semi-detached",
    year_built: 1975,
    bedrooms: 3,
    sum_insured_buildings: 300000,
    sum_insured_contents: 50000,
    claims_count_5yr: 0,
  });

  const set = (k) => (e) => setFields((f) => ({ ...f, [k]: e.target.value }));

  // Address lookup + prefill (WO S10, interim EPC-API path). Dark-tolerant:
  // a 503 from the bridge means the lookup isn't armed — the manual flow is
  // untouched and no lookup UI error is shown beyond the quiet hint.
  const [lookup, setLookup] = React.useState({ phase: "idle", addresses: [] });
  // idle | searching | found | empty | error | prefilled | dark

  const onFindAddress = async () => {
    setLookup({ phase: "searching", addresses: [] });
    try {
      const resp = await fetch("/api/demo-address", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ k: accessKey, action: "addresses", postcode: fields.postcode }),
      });
      if (resp.status === 503) { setLookup({ phase: "dark", addresses: [] }); return; }
      if (resp.status === 400) { setLookup({ phase: "error", addresses: [], msg: CQ.errorPostcode }); return; }
      if (!resp.ok) { setLookup({ phase: "error", addresses: [], msg: CQ.lookupError }); return; }
      const data = await resp.json();
      const addresses = data.addresses || [];
      setLookup({ phase: addresses.length ? "found" : "empty", addresses });
    } catch (err) {
      setLookup({ phase: "error", addresses: [], msg: CQ.lookupError });
    }
  };

  const onPickAddress = async (e) => {
    const id = e.target.value;
    if (!id) return;
    try {
      const resp = await fetch("/api/demo-address", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ k: accessKey, action: "prefill", certificate_number: id }),
      });
      if (!resp.ok) { setLookup((l) => ({ ...l, phase: "error", msg: CQ.lookupError })); return; }
      const data = await resp.json();
      setFields((f) => ({ ...f, ...(data.prefill || {}) }));
      setLookup((l) => ({ ...l, phase: "prefilled", selected: id }));
    } catch (err) {
      setLookup((l) => ({ ...l, phase: "error", msg: CQ.lookupError }));
    }
  };

  const onSubmit = async (e) => {
    e.preventDefault();
    setState({ phase: "working" });
    try {
      const resp = await fetch("/api/demo-quote", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ k: accessKey, risk: fields }),
      });
      if (resp.status === 400) { setState({ phase: "error", msg: CQ.errorPostcode }); return; }
      if (resp.status === 403) { setState({ phase: "error", msg: CQ.lockedBody }); return; }
      if (!resp.ok) { setState({ phase: "error", msg: CQ.errorGeneric }); return; }
      const data = await resp.json();
      setState({ phase: "done", mode: data.mode, quote: data.quote });
    } catch (err) {
      setState({ phase: "error", msg: CQ.errorGeneric });
    }
  };

  if (!accessKey) {
    return (
      <React.Fragment>
        <PageHero eyebrow={CQ.heroEyebrow} title={CQ.lockedTitle} standfirst={CQ.lockedBody} />
      </React.Fragment>
    );
  }

  return (
    <React.Fragment>
      {/* §2 split hero — content on paper, vetted premium-domestic
          photography bleeding to the right viewport edge (deck 50/50).
          IMG.stucco = the £500k–£1.5m register; BleedMedia carries the
          placeholder chip per §4. */}
      <PageHero
        eyebrow={CQ.heroEyebrow}
        title={CQ.heroTitle}
        standfirst={CQ.heroStandfirst}
        actions={<span className="coming-soon">{CQ.badge}</span>}
        image={IMG.stucco}
        imageCaption={CQ.heroImageCaption}
      />

      <div className="quote-shell">
        <div className="quote-shell__col">
          <header className="page-section__head" style={{ position: "static", marginBottom: 0 }}>
            <div className="page-section__eyebrow">{CQ.formEyebrow}</div>
            <h2 className="page-section__title">{CQ.formTitle}</h2>
          </header>

          <form className="quote-form" onSubmit={onSubmit} data-form="quoteflow-demo">
            <div className="quote-field">
              <label className="quote-field__lbl" htmlFor="qf-postcode">{CQ.postcodeLabel}</label>
              <div style={{ display: "flex", gap: 10, alignItems: "stretch" }}>
                <input id="qf-postcode" name="postcode" type="text" placeholder={CQ.postcodePlaceholder} required value={fields.postcode} onChange={set("postcode")} style={{ textTransform: "uppercase", flex: 1 }} autoComplete="off" />
                {lookup.phase !== "dark" && (
                  <button type="button" className="btn" onClick={onFindAddress} disabled={lookup.phase === "searching" || !fields.postcode.trim()}>
                    {lookup.phase === "searching" ? CQ.findingAddress : CQ.findAddress}
                  </button>
                )}
              </div>
              <span className="quote-field__hint">{CQ.postcodeHint}</span>
            </div>

            {(lookup.phase === "found" || lookup.phase === "prefilled") && (
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-address">{CQ.addressLabel}</label>
                <select id="qf-address" defaultValue="" onChange={onPickAddress}>
                  <option value="" disabled>{CQ.addressPlaceholder}</option>
                  {lookup.addresses.map((a) => <option key={a.id} value={a.id}>{a.label}</option>)}
                </select>
                {lookup.phase === "prefilled" && (
                  <span className="quote-field__hint">{CQ.prefillNote}</span>
                )}
              </div>
            )}
            {lookup.phase === "empty" && (
              <span className="quote-field__hint">{CQ.lookupEmpty}</span>
            )}
            {lookup.phase === "error" && (
              <span className="quote-field__hint">{lookup.msg || CQ.lookupError}</span>
            )}

            <div className="quote-form__row">
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-type">{CQ.typeLabel}</label>
                <select id="qf-type" value={fields.property_type} onChange={set("property_type")}>
                  {CQ.typeOptions.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
                </select>
              </div>
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-year">{CQ.yearLabel}</label>
                <input id="qf-year" type="number" min="1600" max="2030" value={fields.year_built} onChange={set("year_built")} />
              </div>
            </div>

            <div className="quote-form__row">
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-beds">{CQ.bedroomsLabel}</label>
                <input id="qf-beds" type="number" min="0" max="20" value={fields.bedrooms} onChange={set("bedrooms")} />
              </div>
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-claims">{CQ.claimsLabel}</label>
                <input id="qf-claims" type="number" min="0" max="20" value={fields.claims_count_5yr} onChange={set("claims_count_5yr")} />
              </div>
            </div>

            <div className="quote-form__row">
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-build">{CQ.buildingsLabel}</label>
                <input id="qf-build" type="number" min="50000" max="5000000" step="10000" value={fields.sum_insured_buildings} onChange={set("sum_insured_buildings")} />
              </div>
              <div className="quote-field">
                <label className="quote-field__lbl" htmlFor="qf-cont">{CQ.contentsLabel}</label>
                <input id="qf-cont" type="number" min="0" max="500000" step="5000" value={fields.sum_insured_contents} onChange={set("sum_insured_contents")} />
              </div>
            </div>

            <button type="submit" className="btn btn--primary" style={{ alignSelf: "flex-start" }} disabled={state.phase === "working"}>
              {state.phase === "working" ? CQ.working : CQ.submit}
            </button>

            {state.phase === "error" && (
              <p role="status" className="form-response">{state.msg}</p>
            )}
          </form>
        </div>

        <aside className="quote-shell__col quote-shell__col--aside">
          <div className="quote-shell__waitlist">
            {state.phase === "done"
              ? <QuoteflowResult mode={state.mode} quote={state.quote} />
              : (
                <React.Fragment>
                  <div style={{ width: 64, borderTop: "2px solid var(--accent)" }} />
                  <span className="page-section__eyebrow">{CQ.resultEyebrow}</span>
                  <p className="page-section__standfirst" style={{ color: "var(--fg-muted)" }}>
                    {state.phase === "working" ? CQ.working : CQ.resultPlaceholder}
                  </p>
                </React.Fragment>
              )}
          </div>
        </aside>
      </div>

      {lookup.phase !== "dark" && (
        <p className="qf-attribution">{CQ.attribution}</p>
      )}
    </React.Fragment>
  );
};

Object.assign(window, { QuoteflowPage });
