/* London Free Guide — Free London Calendar page.
   Renders window.LFG_CALENDAR month by month. Opens on the current month and
   runs to year-end; earlier months sit below under "Earlier in the year" for
   completeness and SEO. A sticky rail jumps between months and filters by
   category; two views (cards / agenda) are offered as a light exploration. */
const { useEffect: useCalEffect, useState: useCalState, useMemo: useCalMemo, useRef: useCalRef } = React;

const CAL = window.LFG_CALENDAR || { year: 2026, months: [] };
const CAL_CATS = window.LFG_CAL_CATS || {};

const CAL_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#E63B2E",
  "view": "cards",
  "showPast": true
}/*EDITMODE-END*/;

const CAL_VIEW_HINT = {
  cards: "Browse it like a noticeboard.",
  agenda: "Scan the whole year as a list."
};

function calMonthId(key) { return "cal-" + key; }
function calScrollTo(key) {
  const el = document.getElementById(calMonthId(key));
  if (!el) return;
  const top = el.getBoundingClientRect().top + window.pageYOffset - 116;
  window.scrollTo({ top: top < 0 ? 0 : top, behavior: "smooth" });
}
function monthEvents(m, cat) {
  if (!cat) return m.events;
  return m.events.filter((e) => e.cat === cat);
}
function calSearchUrl(ev, year) {
  return "https://www.google.com/search?q=" + encodeURIComponent(ev.name + " London " + year);
}
function calMapUrl(ev) {
  return "https://www.google.com/maps/search/?api=1&query=" + encodeURIComponent(ev.area + " London");
}

/* ---- one event, cards view ---- */
function EventCard({ ev, year }) {
  return (
    <div className="cal-event" data-cat={ev.cat}>
      <div className="cal-ev-top">
        <span className="cal-ev-cat">{CAL_CATS[ev.cat] || ev.cat}</span>
        <span className="cal-ev-when">{ev.when} {year}</span>
      </div>
      <div className="cal-ev-name">{ev.name}</div>
      <div className="cal-ev-area"><span className="pin" aria-hidden="true">📍</span> {ev.area}</div>
      <p className="cal-ev-blurb">{ev.blurb}</p>
      <div className="cal-ev-catch">
        <span className="lbl">The catch</span>
        <span className="txt">{ev.catch}</span>
      </div>
      {ev.movable && <span className="cal-ev-movable">Date shifts each year, check before you go</span>}
      <div className="cal-ev-links">
        {ev.link && <a className="cal-ev-link go" href={ev.link}>{ev.linkLabel || "Read more"} <span aria-hidden="true">→</span></a>}
        <a className="cal-ev-link find" href={calSearchUrl(ev, year)} target="_blank" rel="noopener noreferrer">Find the dates <ExtIcon /></a>
        <a className="cal-ev-link map" href={calMapUrl(ev)} target="_blank" rel="noopener noreferrer">Map <ExtIcon /></a>
      </div>
    </div>
  );
}

/* ---- one event, agenda row ---- */
function AgendaRow({ ev, year }) {
  return (
    <div className="cal-agrow" data-cat={ev.cat}>
      <div className="cal-ag-when">
        <span className="cal-ag-date">{ev.when} {year}</span>
        <span className="cal-ag-cat">{CAL_CATS[ev.cat] || ev.cat}</span>
        {ev.movable && <span className="cal-ag-movable">Date shifts each year</span>}
      </div>
      <div className="cal-ag-main">
        <div className="cal-ag-name">{ev.name}</div>
        <p className="cal-ag-blurb">{ev.blurb}</p>
        <div className="cal-ag-meta">
          <span className="cal-ag-area"><span className="pin" aria-hidden="true">📍</span> {ev.area}</span>
          {ev.link && <a className="cal-ev-link go" href={ev.link}>{ev.linkLabel || "Read more"} <span aria-hidden="true">→</span></a>}
          <a className="cal-ev-link find" href={calSearchUrl(ev, year)} target="_blank" rel="noopener noreferrer">Find the dates <ExtIcon /></a>
          <a className="cal-ev-link map" href={calMapUrl(ev)} target="_blank" rel="noopener noreferrer">Map <ExtIcon /></a>
        </div>
        <p className="cal-ag-catch"><b>The catch</b>{ev.catch}</p>
      </div>
    </div>
  );
}

/* ---- a month band ---- */
function MonthBand({ m, isNow, view, cat, year }) {
  const evs = monthEvents(m, cat);
  if (!evs.length) return null;
  return (
    <section className={"cal-month" + (isNow ? " now" : "")} id={calMonthId(m.key)}>
      <div className="cal-month-head">
        <h3 className="cal-month-name">{m.name}</h3>
        <span className="cal-month-year">{year}</span>
        <span className="cal-season">{m.season}</span>
        {isNow && <span className="cal-now-pill">On now</span>}
        <span className="cal-month-count">{evs.length} free {evs.length === 1 ? "thing" : "things"}</span>
        {m.hook && <span className="cal-hook">{m.hook}</span>}
      </div>
      {view === "cards"
        ? <div className="cal-events">{evs.map((e) => <EventCard key={e.name} ev={e} year={year} />)}</div>
        : <div className="cal-agenda">{evs.map((e) => <AgendaRow key={e.name} ev={e} year={year} />)}</div>}
    </section>
  );
}

/* ---- sticky rail: month jump chips + category filter ---- */
function CalRail({ months, nowIdx, activeKey, cat, setCat, showPast }) {
  const cats = Object.keys(CAL_CATS);
  return (
    <div className="cal-rail">
      <div className="wrap cal-rail-inner">
        <div className="cal-months" role="tablist" aria-label="Jump to a month">
          {months.map((m, i) => {
            const past = i < nowIdx;
            const dim = (!showPast && past) || (cat && !monthEvents(m, cat).length);
            return (
              <button key={m.key}
                className={"cal-mchip" + (i === nowIdx ? " now" : "") + (activeKey === m.key ? " active" : "") + (dim ? " dim" : "")}
                onClick={() => calScrollTo(m.key)}
                aria-label={"Jump to " + m.name}>
                {m.name.slice(0, 3)}
              </button>
            );
          })}
        </div>
        <div className="cal-cats" role="group" aria-label="Filter by type">
          <span className="cal-cats-lbl">Filter</span>
          <button className={"cal-catchip" + (!cat ? " on" : "")} onClick={() => setCat(null)}>All</button>
          {cats.map((c) => (
            <button key={c} className={"cal-catchip" + (cat === c ? " on" : "")} onClick={() => setCat(cat === c ? null : c)}>
              {CAL_CATS[c]}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ---- Event JSON-LD: public, answer-shaped, recurring free fixtures ---- */
function CalendarSchema({ months }) {
  useCalEffect(() => {
    const items = [];
    const sNowIdx = new Date().getMonth();
    const sCurYear = new Date().getFullYear();
    months.forEach((m, mi) => {
      const y = mi >= sNowIdx ? sCurYear : sCurYear + 1;
      m.events.forEach((e) => {
        items.push({
          "@type": "Event",
          name: e.name,
          description: e.blurb + " " + e.catch,
          isAccessibleForFree: true,
          eventAttendanceMode: "https://schema.org/OfflineEventAttendanceMode",
          temporalCoverage: m.name + " " + y,
          location: { "@type": "Place", name: e.area, address: { "@type": "PostalAddress", addressLocality: "London", addressCountry: "GB" } },
          offers: { "@type": "Offer", price: "0", priceCurrency: "GBP", availability: "https://schema.org/InStock" }
        });
      });
    });
    const data = {
      "@context": "https://schema.org",
      "@type": "ItemList",
      name: "The free London calendar — free things to do every month",
      description: "A month-by-month guide to free annual events in London: parades, festivals, blossom and lights, with the honest catch on each.",
      url: window.location.href,
      numberOfItems: items.length,
      itemListElement: items.map((it, i) => ({ "@type": "ListItem", position: i + 1, item: it }))
    };
    let el = document.getElementById("calendar-jsonld");
    if (!el) { el = document.createElement("script"); el.type = "application/ld+json"; el.id = "calendar-jsonld"; document.head.appendChild(el); }
    el.textContent = JSON.stringify(data, null, 2);
  }, [months]);
  return null;
}

function CalView({ value, onChange }) {
  const opts = [{ k: "cards", l: "Cards" }, { k: "agenda", l: "Agenda" }];
  return (
    <div className="cal-switch" role="tablist" aria-label="Choose view">
      {opts.map((o) => (
        <button key={o.k} role="tab" aria-selected={value === o.k}
          className={"cal-switch-btn" + (value === o.k ? " on" : "")}
          onClick={() => onChange(o.k)}>{o.l}</button>
      ))}
    </div>
  );
}

/* "London today" bar — weather on the left; rotating on the right are live
   counts pulled from the dated events feed (window.LFGEvents over LFG_CONTENT):
   what is on today, this weekend, ending Sunday, and the next thing not to miss.
   No tube status here. Items with a zero count drop out on their own. */
function CalNow() {
  const [fc, setFc] = useCalState([]);
  const counts = useCalMemo(() => (window.LFGEvents ? window.LFGEvents.counts() : null), []);
  useCalEffect(() => {
    if (!window.LFGWeather) return;
    let live = true;
    window.LFGWeather.london().then((d) => {
      if (live && d && d.length) setFc(d.slice(0, 3).map((x) => ({ label: window.LFGNow.label(x.date), code: x.code, t: x.t })));
    }).catch(() => {});
    return () => { live = false; };
  }, []);
  if (!window.NowBar) return null;
  const NB = window.NowBar;
  const c = counts || {};
  const items = [];
  if (c.today > 0) items.push(<span><b>{c.today}</b> free {c.today === 1 ? "thing" : "things"} on today</span>);
  if (c.weekend > 0) items.push(<span><b>{c.weekend}</b> free {c.weekend === 1 ? "thing" : "things"} this weekend</span>);
  if (c.endingSunday > 0) items.push(<span>{NB.ic("clock")}<b>{c.endingSunday}</b> ending Sunday</span>);
  if (c.marquee) items.push(<span>{NB.ic("star")}Don't miss: <b>{c.marquee.title}</b></span>);
  return <NowBar forecast={fc} items={items} />;
}

function CalendarApp() {
  const [t, setTweak] = useTweaks(CAL_TWEAK_DEFAULTS);
  const months = CAL.months;
  const now = new Date();
  const nowIdx = now.getMonth();
  const curYear = now.getFullYear();
  const nextYear = curYear + 1;
  const [cat, setCat] = useCalState(null);
  const [activeKey, setActiveKey] = useCalState(months[nowIdx] ? months[nowIdx].key : "jan");

  const ahead = months.filter((m, i) => i >= nowIdx);
  const past = months.filter((m, i) => i < nowIdx);
  const totalAhead = useCalMemo(() => ahead.reduce((n, m) => n + monthEvents(m, cat).length, 0), [ahead, cat]);
  const grandTotal = useCalMemo(() => months.reduce((n, m) => n + m.events.length, 0), [months]);

  useCalEffect(() => {
    document.documentElement.style.setProperty("--lfg-accent", t.accent);
  }, [t.accent]);
  useCalEffect(() => { document.title = "Free things to do in London every month — the free London calendar"; }, []);

  /* scroll-spy: highlight the month currently in view in the rail */
  useCalEffect(() => {
    const secs = Array.from(document.querySelectorAll(".cal-month"));
    if (!secs.length) return;
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((en) => {
        if (en.isIntersecting) {
          const id = en.target.id.replace("cal-", "");
          setActiveKey(id);
        }
      });
    }, { rootMargin: "-120px 0px -65% 0px", threshold: 0 });
    secs.forEach((s) => obs.observe(s));
    return () => obs.disconnect();
  }, [t.view, cat, t.showPast]);

  /* honour a #cal-xxx deep link on load */
  useCalEffect(() => {
    const h = (window.location.hash || "").replace("#", "");
    if (h && document.getElementById(h)) {
      setTimeout(() => {
        const el = document.getElementById(h);
        const top = el.getBoundingClientRect().top + window.pageYOffset - 116;
        window.scrollTo({ top: top < 0 ? 0 : top });
      }, 120);
    }
  }, []);

  const nowMonth = months[nowIdx];

  return (
    <React.Fragment>
      <CalendarSchema months={months} />
      <Nav />
      <CalNow />
      <main>
        <section className="cal-hero">
          <div className="wrap">
            <span className="cal-hero-eyebrow">what's free, month by month</span>
            <h1 className="cal-hero-title">A FREE LONDON <span className="r">YEAR</span></h1>
            <p className="cal-hero-sub">The parades, festivals, blossom and lights that come round every year, and never cost a penny to enjoy. We start on this month and roll right through the next twelve, with the honest catch on every one.</p>
            <div className="cal-hero-meta">
              <span className="cal-hero-count">{grandTotal} free fixtures</span>
              {nowMonth && <span className="cal-hero-now">On now in <b>{nowMonth.name} {curYear}</b> · {monthEvents(nowMonth, null).length} things</span>}
              {nowMonth && <a className="cal-hero-jump" href={"#" + calMonthId(nowMonth.key)} onClick={(e) => { e.preventDefault(); calScrollTo(nowMonth.key); }}>Jump to {nowMonth.name} ↓</a>}
            </div>
          </div>
        </section>

        <CalRail months={months} nowIdx={nowIdx} activeKey={activeKey} cat={cat} setCat={setCat} showPast={t.showPast} />

        <div className="wrap cal-controls">
          <CalView value={t.view} onChange={(v) => setTweak("view", v)} />
          <span className="cal-controls-hint">{CAL_VIEW_HINT[t.view]}</span>
        </div>

        <div className="wrap cal-body">
          <div className="cal-yearhead">
            <h2>The rest of <span className="r">{curYear}</span></h2>
            <span className="cal-yearhead-sub">{nowMonth ? nowMonth.name : ""} to December {curYear}</span>
          </div>
          {totalAhead === 0 && (
            <div className="cal-empty">Nothing tagged “{cat ? CAL_CATS[cat] : ""}” coming up. Try another filter or clear it.</div>
          )}
          {ahead.map((m) => (
            <MonthBand key={m.key} m={m} isNow={m.key === (nowMonth && nowMonth.key)} view={t.view} cat={cat} year={curYear} />
          ))}

          {t.showPast && past.length > 0 && (
            <React.Fragment>
              <div className="cal-yearhead past">
                <h2>Into <span className="r">{nextYear}</span></h2>
                <span className="cal-yearhead-sub">The same free fixtures, the year's next loop</span>
              </div>
              {past.map((m) => (
                <MonthBand key={m.key} m={m} isNow={false} view={t.view} cat={cat} year={nextYear} />
              ))}
            </React.Fragment>
          )}

          <p className="cal-note"><b>Heads-up:</b> these are recurring annual fixtures, so exact dates shift a little each year. Anything marked “date shifts each year” moves the most, so check the official listing before you set off. Spotted one we have missed? <a href="Work with us.html">Tell us</a>.</p>
        </div>

        <Band />
      </main>
      <Footer />
      <GuideAgent />

      <TweaksPanel>
        <TweakSection label="Calendar view" />
        <TweakRadio label="Layout" value={t.view}
          options={[{ value: "cards", label: "Cards" }, { value: "agenda", label: "Agenda" }]}
          onChange={(v) => setTweak("view", v)} />
        <TweakToggle label="Show earlier months" value={t.showPast}
          onChange={(v) => setTweak("showPast", v)} />

        <TweakSection label="Brand accent" />
        <TweakColor label="Accent colour" value={t.accent}
          options={["#E63B2E", "#FF6A1A", "#1F8A5B", "#2A6FDB"]}
          onChange={(v) => setTweak("accent", v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

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