// App.jsx — Templates browser page
//
// Layout rhythm: every 6 templates we break the grid with a "Browse by …"
// carousel. After the first 6: Browse by campaign phase (5 phase cards).
// After the next 6: Browse by audience (4 audience cards). After the next
// 6: Starter packs (5 phase-named packs). Browsing-by sections also act
// as filters — clicking a card filters the grid by that phase or audience
// and pauses the breakers (any active filter hides them).

const useCases = window.TEMPLATES;

// When viewing "All phases" (no filters), surface the six hero templates
// first in this exact order, then the rest in the priority order below
// (anything not listed falls through in manifest order).
const ALL_PHASES_PRIORITY = [
"Capture Early Visitor Interest",
"Turn Your Agenda Launch Into Registrations",
"The Final Countdown to Registration",
"Win Back Past Exhibitors",
"Greet and Guide Day-One Arrivals",
"Enable Exhibitors to Drive Their Own Registrations",
"Push Fence-Sitters Over the Line",
"Lock in Next Year's Exhibitors Early",
"Build Your Audience Before the Agenda",
"Sell Your Event With the Full Lineup",
"Open Your Speaker Submissions",
"Open the Sponsor Conversation",
"Convert Wavering Exhibitors With Floor Proof",
"Drive Registrations Off Your Agenda Reveal",
"Get Exhibitor Teams Show-Ready",
"Prove the Quality of Your Floor",
"Drive Workshop Promotion",
"Drive Networking Promotion",
"Earn a Second Wave of Post-Show Shares",
"Turn Highlights Into Super Early Bird Sign-Ups",
"Turn Registered Attendees Into Prepared Attendees",
"Post Event Thank You Placeholder"];

const sortByPriority = (list) => {
  const idx = (t) => {
    const i = ALL_PHASES_PRIORITY.indexOf(t.title);
    return i === -1 ? 999 : i;
  };
  return list.slice().sort((a, b) => idx(a) - idx(b));
};

// Phase filter — order matters and "all" is first.
const phaseFilters = [{ id: "all", label: "All phases" }].concat(
  window.PHASES.slice().sort((a, b) => a.order - b.order).map((p) => ({ id: p.id, label: p.label, window: p.window }))
);

// Audience filter — skip "all"; selecting nothing means no audience filter.
const audienceFilterIds = ["attendees", "exhibitors", "sponsors", "speakers"];

// Phase-card metadata for the "Browse by campaign phase" carousel.
// Tones cycle the same warm palette the audience cards use.
const phaseTones = { "launch": "yellow", "vis-promo": "teal", "reg-drive": "orange", "showtime": "yellow", "post-event": "teal" };

// ─────────────────────────────────────────────────────────────────
// URL state — filters and modals live in the query string (History API).
// Filter params: ?phase=launch&audience=attendees&page=homepage&q=text
// Modal params:  ?template=<id>   or   ?pack=<phaseId>
// Defaults are omitted from the URL so it stays clean until something
// is actually narrowed.
// ─────────────────────────────────────────────────────────────────
const PHASE_IDS = window.PHASES.map((p) => p.id);
const AUDIENCE_IDS = Object.keys(window.AUDIENCES).filter((k) => k !== "all");
const PAGE_IDS = Object.keys(window.PAGES);
const TEMPLATE_BY_ID = Object.fromEntries(window.TEMPLATES.map((t) => [t.id, t]));

function readFiltersFromQuery() {
  const q = new URLSearchParams(window.location.search);
  const phase = q.get("phase");
  const audience = q.get("audience");
  const page = q.get("page");
  const search = q.get("q") || "";
  return {
    phase: phase && PHASE_IDS.includes(phase) ? phase : "all",
    audience: audience && AUDIENCE_IDS.includes(audience) ? audience : null,
    page: page && PAGE_IDS.includes(page) ? page : null,
    search
  };
}

function readModalFromQuery() {
  const q = new URLSearchParams(window.location.search);
  const templateId = q.get("template");
  if (templateId && TEMPLATE_BY_ID[templateId]) return { kind: "template", id: templateId };
  const packId = q.get("pack");
  if (packId && PHASE_IDS.includes(packId)) return { kind: "pack", id: packId };
  return { kind: null, id: null };
}

function buildUrl(filters, modal) {
  const q = new URLSearchParams();
  if (filters.phase && filters.phase !== "all") q.set("phase", filters.phase);
  if (filters.audience) q.set("audience", filters.audience);
  if (filters.page) q.set("page", filters.page);
  if (filters.search) q.set("q", filters.search);
  if (modal && modal.kind === "template" && modal.id) q.set("template", modal.id);else
  if (modal && modal.kind === "pack" && modal.id) q.set("pack", modal.id);
  const qs = q.toString();
  return window.location.pathname + (qs ? "?" + qs : "");
}

function writeUrlState(filters, modal, { replace = true } = {}) {
  const url = buildUrl(filters, modal);
  // Don't push history entries for filter chip clicks — too chatty. Reserve
  // history pushes for modal opens (back button = close modal, not undo filter).
  if (replace) window.history.replaceState(null, "", url);else
  window.history.pushState(null, "", url);
}

function writeFiltersToQuery(f, { replace = true } = {}) {
  writeUrlState(f, readModalFromQuery(), { replace });
}

const FLOWS_IMPORT_URL = "https://app.reelflow.com/dashboard/flows";

function openFlowsWithSlugs(slugs) {
  const ids = (Array.isArray(slugs) ? slugs : [slugs]).filter(Boolean);
  if (!ids.length) return;
  window.location.href = FLOWS_IMPORT_URL + "?template_ids=" + ids.join(",");
}

// Reconstruct the pack object the StarterPackModal expects, given a phase id.
// Mirrors the shape produced inline by StarterPacksCarousel in Cards.jsx so
// deep-link opens behave identically to clicking the carousel card.
function buildPackForPhase(phaseId) {
  const p = window.PHASES.find((x) => x.id === phaseId);
  if (!p) return null;
  const items = window.TEMPLATES.filter((t) => t.phase === phaseId);
  return {
    name: p.label + " Pack",
    phaseId: p.id,
    description: p.packDescription || p.job,
    window: p.window,
    templates: items,
    variants: items.slice(0, 3).map((t) => Math.max(0, Math.min(5, ((t.videos || []).length || 1) - 1)))
  };
}

function App() {
  // Initial state derived from URL so deep-links land directly on the
  // correct view (no flash of unfiltered + closed-modal).
  const initFilters = readFiltersFromQuery();
  const initModal = readModalFromQuery();

  const [search, setSearch] = React.useState(initFilters.search);
  const [activePhase, setActivePhase] = React.useState(initFilters.phase);
  const [activeAudience, setActiveAudience] = React.useState(initFilters.audience);
  const [activePage, setActivePage] = React.useState(initFilters.page);
  const [selectedTemplate, setSelectedTemplate] = React.useState(
    initModal.kind === "template" ? { ...TEMPLATE_BY_ID[initModal.id], variant: 0 } : null
  );
  const [selectedPack, setSelectedPack] = React.useState(
    initModal.kind === "pack" ? buildPackForPhase(initModal.id) : null
  );
  // Height reported by the standalone page running inside the modal iframe —
  // lets the overlay hug the card exactly instead of showing a fixed window.
  const [modalIframeHeight, setModalIframeHeight] = React.useState(420);
  // Whether the iframe has actual content to show yet — until then we show
  // a spinner over the backdrop instead of a blank white box.
  const [modalLoaded, setModalLoaded] = React.useState(false);

  // The modal's actual content now lives on a dedicated /event/* page (see
  // standalone.jsx) so Google can index it as its own document instead of a
  // near-duplicate of this gallery. We show that page in an iframe here,
  // styled to sit inside the same dark backdrop the inline modal used to use.
  const modalHref = selectedTemplate
    ? "event/" + selectedTemplate.id
    : selectedPack
    ? "event/pack/" + selectedPack.phaseId
    : null;

  // Listen for the standalone page asking to close itself, resize, or swap
  // to a different template (clicking a "More like this" card inside it).
  React.useEffect(() => {
    const onMessage = (e) => {
      if (!e.data || typeof e.data !== "object") return;
      if (e.data.type === "reelflow:closeModal") {
        setSelectedTemplate(null);
        setSelectedPack(null);
      } else if (e.data.type === "reelflow:modalHeight" && typeof e.data.height === "number") {
        setModalIframeHeight(e.data.height);
        setModalLoaded(true);
      } else if (e.data.type === "reelflow:openTemplate" && TEMPLATE_BY_ID[e.data.id]) {
        setSelectedTemplate({ ...TEMPLATE_BY_ID[e.data.id], variant: 0 });
      } else if (e.data.type === "reelflow:useTemplate") {
        // Handed off from the iframe rather than tracked+navigated there: this
        // page's GTM has been loaded since the gallery first opened, while the
        // iframe's only started loading when the modal did — firing here
        // avoids a race where a fast click navigates away before a freshly
        // opened iframe's GTM has even finished loading.
        if (e.data.track && window.trackEvent) window.trackEvent(e.data.track.name, e.data.track.params);
        if (Array.isArray(e.data.ids) && e.data.ids.length) openFlowsWithSlugs(e.data.ids);
      }
    };
    window.addEventListener("message", onMessage);
    return () => window.removeEventListener("message", onMessage);
  }, []);

  // Sync filter state → URL query string (replaceState, no history bloat).
  React.useEffect(() => {
    writeFiltersToQuery({ phase: activePhase, audience: activeAudience, page: activePage, search });
  }, [activePhase, activeAudience, activePage, search]);

  // Sync modal state → query string (pushState so back button closes modal).
  // Skip the very first render — initial state already reflects the URL.
  const isFirstModalSync = React.useRef(true);
  React.useEffect(() => {
    if (isFirstModalSync.current) {isFirstModalSync.current = false;return;}
    let modal = { kind: null, id: null };
    if (selectedTemplate) modal = { kind: "template", id: selectedTemplate.id };else
    if (selectedPack) modal = { kind: "pack", id: selectedPack.phaseId };
    writeUrlState(
      { phase: activePhase, audience: activeAudience, page: activePage, search },
      modal,
      { replace: false }
    );
  }, [selectedTemplate, selectedPack]);

  // Reset to a sane default height whenever the iframe target changes —
  // otherwise it'd briefly show the previous template's height until the
  // new page reports in.
  React.useEffect(() => {
    setModalIframeHeight(420);
    setModalLoaded(false);
  }, [modalHref]);

  // Listen for back/forward — re-derive both filter state and modal state
  // from the URL.
  React.useEffect(() => {
    const onPop = () => {
      const f = readFiltersFromQuery();
      setActivePhase(f.phase);
      setActiveAudience(f.audience);
      setActivePage(f.page);
      setSearch(f.search);
      const m = readModalFromQuery();
      setSelectedTemplate(m.kind === "template" ? { ...TEMPLATE_BY_ID[m.id], variant: 0 } : null);
      setSelectedPack(m.kind === "pack" ? buildPackForPhase(m.id) : null);
    };
    window.addEventListener("popstate", onPop);
    return () => {
      window.removeEventListener("popstate", onPop);
    };
  }, []);

  const openTemplate = (uc, variant) => setSelectedTemplate({ ...uc, variant });
  const handleUse = (template) => {
    if (template && window.trackEvent) {
      window.trackEvent("use_template_click", {
        template_id: template.id,
        template_title: template.title,
        source: "grid_card"
      });
    }
    openFlowsWithSlugs(template && template.slug);
  };

  const filteredRaw = useCases.filter((uc) => {
    const matchesPhase = activePhase === "all" || uc.phase === activePhase;
    const matchesAudience = !activeAudience || uc.audience === activeAudience;
    const matchesSearch = !search || uc.title.toLowerCase().includes(search.toLowerCase()) || (uc.description || "").toLowerCase().includes(search.toLowerCase());
    return matchesPhase && matchesAudience && matchesSearch;
  });
  // Apply hero-priority order only when no filter is active.
  const filtered = activePhase === "all" && !activeAudience ? sortByPriority(filteredRaw) : filteredRaw;

  const activePhaseDef = window.PHASES.find((p) => p.id === activePhase);
  const isFiltering = activePhase !== "all" || activeAudience !== null;

  // Counts for the breaker-section cards.
  const countByPhase = (id) => useCases.filter((u) => u.phase === id).length;
  const countByAudience = (id) => useCases.filter((u) => u.audience === id).length;

  // Build the interleaved render list: when no filter is active, splice the
  // breaker sections in after every 6 cards. When a filter is active, render
  // the grid straight through with no breakers.
  const renderGridWithBreakers = () => {
    // Chunk filtered results into rows of 6 so we can splice breakers between.
    const chunks = [];
    for (let i = 0; i < filtered.length; i += 6) chunks.push(filtered.slice(i, i + 6));

    // When filtering by a phase, only show that phase's pack. When filtering
    // by audience only, narrow to packs whose phase contains a template
    // matching that audience. When unfiltered, show all 5 phase packs.
    const allPhases = window.PHASES.slice().sort((a, b) => a.order - b.order);
    let packsForRow;
    if (activePhase !== "all") {
      packsForRow = allPhases.filter((p) => p.id === activePhase);
    } else if (activeAudience) {
      packsForRow = allPhases.filter((p) => useCases.some((t) => t.phase === p.id && t.audience === activeAudience));
    } else {
      packsForRow = allPhases;
    }

    return (
      <React.Fragment>
        {chunks.map((chunk, ci) =>
        <React.Fragment key={"chunk-" + ci}>
            <div className="tb-grid">
              {chunk.map((uc, i) => {
              const globalIdx = ci * 6 + i;
              return (
                <UseCaseCard key={uc.id} {...uc} variant={globalIdx % 6} onClick={() => openTemplate(uc, globalIdx % 6)} onUseTemplate={() => handleUse(uc)} />);

            })}
            </div>

            {/* After chunk 0 (rows 1–2): phase carousel — only when not filtering */}
            {ci === 0 && !isFiltering && chunks.length > 1 &&
          <BrowseByCarousel
            title="Organise by campaign phase"
            subtitle="Each phase maps to a different job your homepage and audience pages need to do."
            items={phaseFilters.filter((p) => p.id !== "all").map((p) => ({
              key: p.id,
              kind: "phase",
              label: p.label,
              count: countByPhase(p.id),
              tone: phaseTones[p.id] || "yellow",
              badge: window.PHASES.find((x) => x.id === p.id)?.window,
              onClick: () => setActivePhase(p.id)
            }))} />
          }

            {/* After chunk 1 (rows 3–4): starter packs — when not filtering */}
            {!isFiltering && ci === 1 && chunks.length > 2 && packsForRow.length > 0 &&
          <StarterPacksCarousel
            phases={packsForRow}
            templates={useCases}
            onOpenPack={(pack) => setSelectedPack(pack)} />
          }

            {/* After chunk 2 (rows 5–6): audience carousel — only when not filtering */}
            {ci === 2 && !isFiltering && chunks.length > 3 &&
          <BrowseByCarousel
            title="Organise by audience"
            subtitle="Filter the grid down to templates built for one specific group."
            items={audienceFilterIds.map((id, i) => ({
              key: id,
              kind: "audience",
              label: window.AUDIENCES[id],
              count: countByAudience(id),
              tone: ["yellow", "teal", "orange", "yellow"][i],
              onClick: () => setActiveAudience(id)
            }))} />
          }

            {/* Starter packs also show after row 2 (chunk 0) when filtering. */}
            {isFiltering && ci === 0 && packsForRow.length > 0 &&
          <StarterPacksCarousel
            phases={packsForRow}
            templates={useCases}
            onOpenPack={(pack) => setSelectedPack(pack)} />
          }
          </React.Fragment>
        )}
      </React.Fragment>);

  };

  return (
    <React.Fragment>
      <Header />

      <div className="tb-page">
        <div className="tb-hero-band">
          <div className="tb-hero-band__inner">
            <section className="tb-hero">
              <h1>
                Interactive ReelFlow video templates for event websites
                <em></em>
              </h1>
              <p>Ready-to-go Flow templates for event organisers who want to add engaging interactive video experiences. Drop in videos to go-live on your website in minutes...</p>
            </section>
          </div>
        </div>

        <div className="tb-shell">
          <nav className="tb-crumb" aria-label="Breadcrumb">
              <a href="https://reelflow.com/templates" className="tb-crumb__link">Templates</a>
              <span className="tb-crumb__sep">/</span>
              <a href="#" className="tb-crumb__link" onClick={(e) => {e.preventDefault();setActivePhase("all");setActiveAudience(null);setActivePage(null);setSearch("");}}>Event Organisers</a>
              {activePhase !== "all" &&
              <React.Fragment>
                  <span className="tb-crumb__sep">/</span>
                  <a href="#" className="tb-crumb__link" onClick={(e) => {e.preventDefault();setActivePhase("all");}}>{(window.PHASES.find((p) => p.id === activePhase) || {}).label}</a>
                </React.Fragment>
              }
              {activeAudience &&
              <React.Fragment>
                  <span className="tb-crumb__sep">/</span>
                  <a href="#" className="tb-crumb__link" onClick={(e) => {e.preventDefault();setActiveAudience(null);}}>{window.AUDIENCES[activeAudience]}</a>
                </React.Fragment>
              }
              {activePage &&
              <React.Fragment>
                  <span className="tb-crumb__sep">/</span>
                  <a href="#" className="tb-crumb__link" onClick={(e) => {e.preventDefault();setActivePage(null);}}>{window.PAGES[activePage]}</a>
                </React.Fragment>
              }
              {search &&
              <React.Fragment>
                  <span className="tb-crumb__sep">/</span>
                  <a href="#" className="tb-crumb__link" onClick={(e) => {e.preventDefault();setSearch("");}}>"{search}"</a>
                </React.Fragment>
              }
            </nav>
          <section className="tb-section">
            <div className="tb-section__head">
              <div>
                <h2>Pick an event campaign phase...</h2>
              </div>
              <div className="tb-section__filters">
                {phaseFilters.map((f) =>
                <button
                  key={f.id}
                  className={"tb-chip" + (activePhase === f.id ? " is-active" : "")}
                  onClick={() => setActivePhase(f.id)}
                  title={f.window || ""}>
                    {f.label}
                  </button>
                )}
              </div>
            </div>

            {/* Active-filter context line */}
            {(activePhaseDef || activeAudience) &&
            <div className="tb-active-filters">
                {activeAudience &&
              <span className="tb-active-pill">
                    Audience: <strong>{window.AUDIENCES[activeAudience]}</strong>
                    <button className="tb-active-pill__clear" onClick={() => setActiveAudience(null)} aria-label="Clear audience filter">×</button>
                  </span>
              }
              </div>
            }

            {renderGridWithBreakers()}

            {filtered.length === 0 &&
            <p className="tb-empty">No templates match these filters.</p>
            }
          </section>
        </div>
      </div>

      {modalHref &&
      <div className="tb-modal-backdrop" onClick={() => { setSelectedTemplate(null); setSelectedPack(null); }}>
        {!modalLoaded &&
        <div className="tb-modal-loading" aria-hidden="true">
          <span className="tb-modal-spinner"></span>
        </div>
        }
        <iframe
          key={modalHref}
          className={"tb-modal-iframe" + (modalLoaded ? " is-loaded" : "")}
          src={modalHref}
          style={{ height: modalIframeHeight }}
          title={selectedTemplate ? selectedTemplate.title : selectedPack.name}
          onClick={(e) => e.stopPropagation()}
          onLoad={() => setModalLoaded(true)} />

      </div>
      }

      <Footer />
    </React.Fragment>);

}

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