// PageShell.jsx — wraps every inner page with the same chrome
// Manages contact modal state, scroll-pinned nav, cookie banner, scroll-reveal.

function useScrollReveal() {
  React.useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          entry.target.classList.add("in");
          io.unobserve(entry.target);
        }
      });
    }, { threshold: 0.12, rootMargin: "0px 0px -60px 0px" });
    const observe = () => {
      document.querySelectorAll(".reveal:not(.in)").forEach((el) => io.observe(el));
    };
    observe();
    const mo = new MutationObserver(observe);
    mo.observe(document.body, { childList: true, subtree: true });
    return () => { io.disconnect(); mo.disconnect(); };
  }, []);
}

const PageShell = ({ active, children }) => {
  const [scrolled, setScrolled] = React.useState(false);
  const [contact, setContact] = React.useState({ open: false, prefill: "" });
  const [showCookie, setShowCookie] = React.useState(
    () => localStorage.getItem("acerti-cookie-consent") === null
  );
  const [orange, setOrange] = React.useState(
    (document.documentElement.dataset.orange) || "generous"
  );
  useScrollReveal();

  React.useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  // Apply orange intensity to <html> for the CSS knobs in extras.css
  React.useEffect(() => {
    document.documentElement.dataset.orange = orange;
  }, [orange]);

  const openContact = (prefill = "") => setContact({ open: true, prefill });

  // expose globally so deep children can open the modal w/o prop drilling
  React.useEffect(() => {
    window.__openContact = openContact;
    return () => { delete window.__openContact; };
  }, []);

  const backToTop = () => window.scrollTo({ top: 0, behavior: "smooth" });

  // The TweaksPanel may not be loaded on every page; guard against undefined.
  const TweaksPanelImpl = (typeof window !== "undefined") ? window.TweaksPanel : null;

  return (
    <>
      <TopNav active={active} scrolled={scrolled} onContact={() => openContact("")} />
      <main>{children}</main>
      <Footer />
      {showCookie && <CookieBanner onDismiss={(choice) => { localStorage.setItem("acerti-cookie-consent", choice || "accepted"); setShowCookie(false); }} />}
      <ContactModal
        open={contact.open}
        prefill={contact.prefill}
        onClose={() => setContact({ open: false, prefill: "" })}
      />

      {TweaksPanelImpl && (
        <TweaksPanelImpl title="Tweaks">
          <window.TweakSection label="Voltage" />
          <window.TweakRadio
            label="Orange accent"
            value={orange}
            options={["scarce", "balanced", "generous"]}
            onChange={(v) => setOrange(v)}
          />

          <window.TweakSection label="Actions" />
          <window.TweakButton label="Open contact form" onClick={() => openContact("")} />
          <window.TweakButton label="Back to top" onClick={backToTop} />
        </TweaksPanelImpl>
      )}
    </>
  );
};

// Generic inner-page hero used by all sub-pages.
// `art` may be a React element OR a string key into window.PageHeroArts.
const PageHero = ({ chip, breadcrumb, title, lede, ctaLabel = "CONTACT US", secondaryLabel, secondaryHref, art }) => {
  // Resolve string art keys to their component from the registry
  let resolvedArt = art;
  if (typeof art === "string" && window.PageHeroArts && window.PageHeroArts[art]) {
    const ArtComp = window.PageHeroArts[art];
    resolvedArt = <ArtComp />;
  }
  return (
    <section className={"container page-hero" + (resolvedArt ? " has-art" : "")}>
      <div className="page-hero-copy">
        {breadcrumb && (
          <div className="breadcrumb">
            {breadcrumb.map((b, i) => (
              <React.Fragment key={i}>
                {b.href ? <a href={b.href}>{b.label}</a> : <span>{b.label}</span>}
                {i < breadcrumb.length - 1 && <span className="sep">/</span>}
              </React.Fragment>
            ))}
          </div>
        )}
        {chip && <div className="inner-chip">{chip}</div>}
        <h1 className="display-xl reveal">{title}</h1>
        {lede && <p className="lede reveal" data-d="1">{lede}</p>}
        <div className="hero-cta reveal" data-d="2" style={{ marginTop: 32 }}>
          <button className="btn btn-primary" onClick={() => window.__openContact && window.__openContact("")}>{ctaLabel}</button>
          {secondaryLabel && (
            secondaryHref
              ? <a className="btn btn-secondary" href={secondaryHref}>{secondaryLabel}</a>
              : <button className="btn btn-secondary">{secondaryLabel}</button>
          )}
        </div>
      </div>
      {resolvedArt && (
        <div className="page-hero-art reveal" data-d="2">{resolvedArt}</div>
      )}
    </section>
  );
};

// Generic spec list — used in solution-detail pages
const SpecList = ({ items }) => (
  <div className="spec-list">
    {items.map((it, i) => (
      <div className="spec reveal" data-d={(i % 4) + 1} key={it.title}>
        <div className="n">{String(i + 1).padStart(2, "0")}</div>
        <div>
          <h5 className="title-md">{it.title}</h5>
          <p>{it.body}</p>
        </div>
      </div>
    ))}
  </div>
);

// AI process flow box
const AIFlow = ({ title = "How AI shows up here", steps }) => (
  <div className="ai-flow reveal">
    <div className="caption-up" style={{ color: "var(--muted)", marginBottom: 8 }}>{title}</div>
    {steps.map((s, i) => (
      <div className="row" key={s.title}>
        <div className="n">{String(i + 1).padStart(2, "0")}</div>
        <div>
          <div className="lbl">{s.title}</div>
          <div className="sub">{s.body}</div>
        </div>
        <div className="arr">{i === steps.length - 1 ? "\u2713" : "\u2192"}</div>
      </div>
    ))}
  </div>
);

// Case-study card
const CaseStudy = ({ kicker, quote, name, role, results }) => (
  <div className="case-card reveal">
    <div>
      <div className="kicker">{kicker}</div>
      <div className="quote">&ldquo;{quote}&rdquo;</div>
      <div className="meta">{name} &middot; {role}</div>
    </div>
    <div className="results">
      {results.map((r) => (
        <div key={r.label}>
          <div className="num">{r.value}</div>
          <div className="res-lbl">{r.label}</div>
        </div>
      ))}
    </div>
  </div>
);

// Related solutions strip
const RelatedSolutions = ({ items }) => (
  <section className="container section" style={{ paddingTop: 0 }}>
    <div className="section-header reveal" style={{ marginBottom: 24 }}>
      <div className="eyebrow"><span className="mark"/>CONTINUE EXPLORING</div>
      <h3 className="display-md">Related practice areas.</h3>
    </div>
    <div className="related-grid">
      {items.map((r) => (
        <a key={r.name} href={r.href} className="related-card reveal" data-d="1">
          <span className="rel-tag">{r.tag}</span>
          <span className="rel-name">{r.name}</span>
          <span className="rel-arr">Learn more &rarr;</span>
        </a>
      ))}
    </div>
  </section>
);

// Common pre-footer callout — reuses kit Callout
const InlineCTA = ({ title, body, label = "Start a conversation" }) => (
  <section className="container" style={{ marginTop: 32, marginBottom: 32 }}>
    <div className="callout reveal">
      <div>
        <h2 className="display-md">{title}</h2>
        {body && <p>{body}</p>}
      </div>
      <button className="btn btn-on-coral" onClick={() => window.__openContact && window.__openContact("")}>
        {label} <span className="callout-arrow">&rarr;</span>
      </button>
    </div>
  </section>
);

// Animated count-up used inside SpecStrip when a value is numeric
const SpecAnimatedNumber = ({ target, decimals = 0, prefix = "", suffix = "", duration = 1600 }) => {
  const [val, setVal] = React.useState(0);
  const ref = React.useRef(null);
  const started = React.useRef(false);
  React.useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting && !started.current) {
          started.current = true;
          let raf, startTs;
          const step = (ts) => {
            if (!startTs) startTs = ts;
            const p = Math.min(1, (ts - startTs) / duration);
            const eased = 1 - Math.pow(1 - p, 3);
            setVal(target * eased);
            if (p < 1) raf = requestAnimationFrame(step);
          };
          raf = requestAnimationFrame(step);
        }
      });
    }, { threshold: 0.3 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [target, duration]);
  return <span ref={ref}>{prefix}{val.toFixed(decimals)}{suffix}</span>;
};

// Match optional sign/prefix + a number + optional non-digit suffix (used for count-up)
const SPEC_NUMERIC_RE = /^([~≈+\-]?)(\d+(?:\.\d+)?)([^\d.]*)$/;
// Broader classifier — anything starting with a digit (with optional sign / comparator)
// counts as a "KPI" value and stays in the bottom row. Animation only fires when the
// strict regex matches cleanly.
const isSpecNumeric = (raw) => /^[~≈+\-<>]?\d/.test(String(raw ?? "").trim());

// Parse a value like "~30%", "100%", "20+", "4.2s", "2M+", "USA" → returns a node.
const renderSpecValue = (raw) => {
  if (raw == null) return null;
  const s = String(raw);
  const m = s.match(SPEC_NUMERIC_RE);
  if (!m) return s; // not animatable (e.g. "USA", "2M+")
  const [, prefix, numStr, suffix] = m;
  const target = parseFloat(numStr);
  const decimals = (numStr.split(".")[1] || "").length;
  return (
    <SpecAnimatedNumber target={target} decimals={decimals} prefix={prefix} suffix={suffix} />
  );
};

// Spec strip — orange-soft card with large serif numbers.
// Items whose value is non-numeric (e.g. a platform list) render as a full-width
// header row above the KPI row, so long text doesn't crowd a single cell.
const SpecStrip = ({ items }) => {
  const textItems = items.filter((s) => !isSpecNumeric(s.val));
  const kpiItems  = items.filter((s) =>  isSpecNumeric(s.val));
  return (
    <section className="container reveal" style={{ paddingTop: 24, paddingBottom: 24 }}>
      <div className="stats spec-stats">
        {textItems.length > 0 && (
          <div className="spec-textrow reveal">
            {textItems.map((s) => (
              <div className="spec-textitem" key={s.lbl}>
                <div className="spec-textlbl">{s.lbl}</div>
                <div className="spec-textval">{s.val}</div>
              </div>
            ))}
          </div>
        )}
        {kpiItems.map((s, i) => (
          <div className="reveal spec-kpi" data-d={i} key={s.lbl}>
            <div className="stat-num">{renderSpecValue(s.val)}</div>
            <div className="stat-label">{s.lbl}</div>
          </div>
        ))}
      </div>
    </section>
  );
};

// FAQ block
const FAQ = ({ items }) => (
  <div className="faq reveal">
    {items.map((q) => (
      <details key={q.q}>
        <summary>{q.q}</summary>
        <p>{q.a}</p>
      </details>
    ))}
  </div>
);

Object.assign(window, {
  PageShell, PageHero, SpecList, AIFlow, CaseStudy,
  RelatedSolutions, InlineCTA, SpecStrip, FAQ, useScrollReveal,
});
