// app.jsx — orchestrator: mode toggle (website/agent), view routing between
// the main pyramid, tier modals, stream funnels, the DNA assessment, the
// sub-pyramids, and the lens overlay.

const { useState, useEffect } = React;

function L180App() {
  const [mode, setMode] = useState("website");        // website | agent
  const [lens, setLens] = useState("process");         // process | services — the framing axis
  const [view, setView] = useState({ type: "main" }); // main | funnel | dna | subpyramid | lens
  const [modalTier, setModalTier] = useState(null);   // tier number or null
  const [edu, setEdu] = useState(null);               // { stream, hue } or null — layered edu pop-up
  const [service, setService] = useState(null);        // { svc, hue } or null — layered service pop-up

  // open the calendar scheduler in a new tab (no-op until the URL is set)
  function openSchedule() {
    const url = window.L180_SCHEDULE_URL;
    if (url) window.open(url, "_blank", "noopener,noreferrer");
  }

  const tiers = window.L180_TIERS;                     // the PROCESS pyramid (5 tiers)
  const serviceTiers = window.L180_SERVICE_TIERS;       // the SERVICES pyramid (3 bands)
  // which triangle is on screen — Services swaps in its own band set
  const activeTiers = lens === "services" ? serviceTiers : tiers;
  const orderTop = activeTiers;                         // top->bottom
  // routing always resolves against the PROCESS set (services route INTO process streams)
  const tierByN = (n) => tiers.find((t) => t.n === n);
  // the open modal belongs to whichever pyramid is showing
  const activeTierByN = (n) => activeTiers.find((t) => t.n === n);

  // lock background scroll while an overlay view is active
  useEffect(() => {
    const overlay = view.type !== "main";
    document.body.style.overflow = overlay ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [view]);

  // deep-link: open a tier modal from a #tier-N hash (e.g. homepage pyramid CTAs).
  // Tier-N refers to the PROCESS pyramid, so force that lens before opening.
  useEffect(() => {
    function fromHash() {
      const m = (window.location.hash || "").match(/tier-(\d)/);
      if (m) { const n = +m[1]; if (tiers.find((t) => t.n === n)) { setLens("process"); setModalTier(n); } }
    }
    fromHash();
    window.addEventListener("hashchange", fromHash);
    return () => window.removeEventListener("hashchange", fromHash);
  }, []);

  function openTier(n) { setModalTier(n); }
  function closeModal() { setModalTier(null); }

  // switching pyramids: band n (1-3) collides with process tier n, so drop any
  // open overlay to avoid a stale modal mis-resolving against the new set.
  function switchLens(next) {
    if (next === lens) return;
    setModalTier(null); setEdu(null); setService(null);
    setLens(next);
  }

  // tier prev/next within the modal (by visual stack: down = lower tier),
  // computed against whichever pyramid is currently showing
  const modalIdx = modalTier ? activeTiers.findIndex((t) => t.n === modalTier) : -1;
  const lowerTier = modalIdx >= 0 && modalIdx < activeTiers.length - 1 ? activeTiers[modalIdx + 1].n : null;
  const higherTier = modalIdx > 0 ? activeTiers[modalIdx - 1].n : null;

  function onStream(tier, stream) {
    closeModal();
    // external experience — open the hosted tool in a new tab
    if (stream.link) { window.open(stream.link, "_blank", "noopener,noreferrer"); return; }
    if (stream.funnelType === "schedule") { openSchedule(); return; }
    if (stream.funnelType === "dna" || stream.isGateway) { setView({ type: "dna" }); return; }
    // Any pathway with a built sub-pyramid (the four Designs + the shared
    // Growth Layer) goes directly to its sub-pyramid map.
    if (window.L180_SUBPYRAMIDS && window.L180_SUBPYRAMIDS[stream.funnelType]) {
      setView({ type: "subpyramid", key: stream.funnelType, result: null });
      return;
    }
    const fkey = window.l180FunnelKeyFor(tier.n, stream.name);
    if (fkey) setView({ type: "engine", tierN: tier.n, stream, fkey });
    else setView({ type: "funnel", tierN: tier.n, stream });
  }

  // service "Get started": resolve a service's route to { tier, stream } and
  // hand it to the existing onStream() pipeline (closes both pop-ups first).
  function startService(svc) {
    setService(null);
    closeModal();
    const r = svc && svc.route;
    if (!r) { openSchedule(); return; }
    const t = tierByN(r.tierN);
    const s = t && t.streams.find((x) => x.name === r.streamName);
    if (t && s) onStream(t, s);
    else openSchedule();
  }

  // open an interactive standalone page (e.g. the Six Jobs wheel) as a
  // full-screen overlay inside the app, instead of a new tab
  function openEmbed(src, title) {
    closeModal();
    setView({ type: "embed", src, title });
  }

  // continue-the-process: jump to the next stream in the same tier
  function continueNext(tierN, stream) {
    const t = tierByN(tierN);
    const idx = t.streams.findIndex((s) => s.name === stream.name);
    const nxt = t.streams[(idx + 1) % t.streams.length];
    onStream(t, nxt);
  }

  function onRoute(result) {
    if (window.L180_SUBPYRAMIDS && window.L180_SUBPYRAMIDS[result.route]) {
      setView({ type: "subpyramid", key: result.route, result });
    } else {
      setView({ type: "lens", result });
    }
  }

  return (
    <div>
      {/* top bar */}
      <div className="l180-topbar">
        <div className="l180-brandmark">
          <img className="l180-logo" src="https://assets.cdn.filesafe.space/YxdIpZn0CCUlZu8wTtjt/media/67d48eab1c7cb784002b20ed.png" alt="LIFE180" />
          <span className="tag">{lens === "services" ? "Our Services" : (mode === "agent" ? "Agent · Conversation Map" : "The Process")}</span>
        </div>
        <div className="l180-topbar-maps">
          <a href="/clients/" target="_blank" className="l180-maplink">Client Map</a>
          <a href="/agents/" target="_blank" className="l180-maplink">Agent Map</a>
        </div>
        <div className="l180-toggles">
          <div className="l180-lenstoggle" title="Process shows how we work, tier by tier. Services shows the same work by the service names you know.">
            <button className={lens === "process" ? "on" : ""} onClick={() => switchLens("process")}>Process</button>
            <button className={lens === "services" ? "on" : ""} onClick={() => switchLens("services")}>Services</button>
          </div>
        </div>
      </div>

      {/* main pyramid (always mounted so scroll position persists) */}
      <L180Pyramid mode={mode} lens={lens} tiers={activeTiers} onOpenTier={openTier} />

      {/* services-lens footer — standalone services that live outside the pyramid */}
      {lens === "services" && (
        <div className="l180-servicefooter">
          <div className="l180-servicefooter-head">Beyond the pyramid</div>
          <div className="l180-servicefooter-cards">
            {window.l180StandaloneServices().map((svc, i) => {
              const h = window.L180_BRAND.risk[(i + 1) % window.L180_BRAND.risk.length];
              return (
                <button key={svc.key} className="l180-servicefootcard" style={{ "--hue": h }}
                        onClick={() => setService({ svc, hue: h })}>
                  <h4>{svc.name}</h4>
                  <p>{svc.tagline}</p>
                  <span className="l180-servicefootcard-cta">Learn more <span className="arr">→</span></span>
                </button>
              );
            })}
          </div>
        </div>
      )}

      {/* tier modal — belongs to whichever pyramid is showing */}
      {modalTier && activeTierByN(modalTier) && (
        <L180TierModal
          tier={activeTierByN(modalTier)}
          mode={mode}
          lens={lens}
          onClose={closeModal}
          onPrev={lowerTier ? () => setModalTier(lowerTier) : null}
          onNext={higherTier ? () => setModalTier(higherTier) : null}
          prevTier={lowerTier ? activeTierByN(lowerTier) : null}
          nextTier={higherTier ? activeTierByN(higherTier) : null}
          onStream={onStream}
          onEdu={(stream, hue) => setEdu({ stream, hue })}
          onService={(svc, hue) => setService({ svc, hue })}
          onSchedule={() => openSchedule()}
          onEmbed={openEmbed}
          paused={!!edu || !!service}
        />
      )}

      {/* service detail pop-up — layered on top of the tier modal / footer */}
      {service && (
        <L180ServiceModal
          service={service.svc}
          hue={service.hue}
          onClose={() => setService(null)}
          onStart={() => startService(service.svc)}
          onSchedule={() => openSchedule()}
        />
      )}

      {/* educational / example pop-up — layered on top of the tier modal */}
      {edu && (
        <L180EduModal
          stream={edu.stream}
          hue={edu.hue}
          onClose={() => setEdu(null)}
          onSchedule={() => openSchedule()}
        />
      )}

      {/* embedded interactive page (e.g. the Six Jobs wheel) */}
      {view.type === "embed" && (
        <div className="l180-embed">
          <button className="l180-embed-close" onClick={() => setView({ type: "main" })} aria-label="Close">
            <span className="arr">←</span> Back
          </button>
          <iframe className="l180-embed-frame" src={view.src} title={view.title || "Interactive"} />
        </div>
      )}

      {/* funnel (generic stub) */}
      {view.type === "funnel" && (
        <L180Funnel
          tier={tierByN(view.tierN)}
          stream={view.stream}
          mode={mode}
          onExit={() => setView({ type: "main" })}
        />
      )}

      {/* funnel (config-driven engine) */}
      {view.type === "engine" && (
        <L180FunnelEngine
          cfg={window.L180_FUNNELS[view.fkey]}
          tier={tierByN(view.tierN)}
          mode={mode}
          onExit={() => setView({ type: "main" })}
          onContinueNext={() => continueNext(view.tierN, view.stream)}
        />
      )}

      {/* DNA assessment */}
      {view.type === "dna" && (
        <L180DNA
          mode={mode}
          onExit={() => setView({ type: "main" })}
          onRoute={onRoute}
        />
      )}

      {/* sub-pyramid */}
      {view.type === "subpyramid" && (
        <L180SubPyramid
          data={window.L180_SUBPYRAMIDS[view.key]}
          result={view.result}
          mode={mode}
          onExit={() => setView({ type: "main" })}
        />
      )}

      {/* lens overlay */}
      {view.type === "lens" && (
        <L180Lens
          result={view.result}
          onExit={() => setView({ type: "main" })}
          onReturn={() => setView({ type: "main" })}
        />
      )}
    </div>
  );
}

// Error boundary: a one-off cold-load race self-heals by retrying instead of
// showing a permanent black screen.
class L180Boundary extends React.Component {
  constructor(p) { super(p); this.state = { err: false, tries: 0 }; }
  static getDerivedStateFromError() { return { err: true }; }
  componentDidCatch() {
    if (this.state.tries < 5) setTimeout(() => this.setState((s) => ({ err: false, tries: s.tries + 1 })), 60);
  }
  render() {
    if (this.state.err) return null;
    return this.props.children;
  }
}

// Mount only once every dependency global is present (guards the babel
// external-script execution race).
function l180Ready() {
  return window.L180_TIERS && window.L180_SUBPYRAMIDS && window.L180_FUNNELS &&
    window.L180_SERVICES && window.l180ServicesForTier &&
    window.L180_SERVICE_TIERS && window.l180BandServices &&
    window.L180Pyramid && window.L180TierModal && window.L180Funnel &&
    window.L180FunnelEngine && window.L180DNA && window.L180SubPyramid &&
    window.L180Lens && window.L180EduModal && window.L180ServiceModal &&
    window.l180TierHue && window.l180ScoreDNA;
}
function l180Mount() {
  if (!l180Ready()) { setTimeout(l180Mount, 30); return; }
  ReactDOM.createRoot(document.getElementById("root")).render(
    <L180Boundary><L180App /></L180Boundary>
  );
}
l180Mount();
