/* London Free Guide — Day Trips page.
   Free days OUT of London. Renders every content item flagged daytrip:true.
   The day out is free; trains aren't — so transport (time + honest fare) is a
   first-class feature on every trip. Three layouts are offered as a live
   exploration (in-page switch + a Tweak): editorial / list / map. */
const { useEffect: useDTEffect } = React;

const DT_COST = window.LFG_COST || {};

const DT_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#E63B2E",
  "layout": "editorial",
  "showTransport": true,
  "showWeather": true
}/*EDITMODE-END*/;

/* mock weather, used as the fallback if the live fetch fails */
const DT_WX = {
  "le-weekend-sandwich": { ic: "⛅", t: 19 },
  "brighton-by-the-sea": { ic: "☀️", t: 21 },
  "whitstable-sunset": { ic: "⛅", t: 20 },
  "margate-turner": { ic: "☀️", t: 21 },
  "cambridge-on-foot": { ic: "🌦️", t: 18 },
  "jane-austen-bath": { ic: "☁️", t: 18 },
  "glastonbury-town": { ic: "🌧️", t: 17 }
};
/* coordinates for the live per-town fetch */
const DT_COORDS = {
  "le-weekend-sandwich": [51.2754, 1.3403],
  "brighton-by-the-sea": [50.8225, -0.1372],
  "whitstable-sunset": [51.3601, 1.0257],
  "margate-turner": [51.3891, 1.3862],
  "cambridge-on-foot": [52.2053, 0.1218],
  "jane-austen-bath": [51.3811, -2.3590],
  "glastonbury-town": [51.1485, -2.7140],
  "canterbury-cathedral-city": [51.2802, 1.0789],
  "rye-medieval-town": [50.9510, 0.7320],
  "seven-sisters-cliffs": [50.7480, 0.1520],
  "cotswolds-villages": [51.9912, -1.7020],
  "salisbury-old-sarum": [51.0688, -1.7945],
  "lavenham-wool-village": [52.1080, 0.7960],
  "saffron-walden-town": [52.0244, 0.2430],
  "st-albans-roman-city": [51.7505, -0.3360],
  "oxford-free-museums": [51.7548, -1.2544],
  "windsor-long-walk": [51.4839, -0.6044],
  "hastings-old-town": [50.8552, 0.5729],
  "stratford-upon-avon": [52.1920, -1.7070],
  "dover-white-cliffs": [51.1295, 1.3360],
  "folkestone-by-the-sea": [51.0810, 1.1700],
  "leigh-on-sea": [51.5419, 0.6553],
  "bexhill-de-la-warr": [50.8403, 0.4675],
  "rochester-kent": [51.3886, 0.5040],
  "royal-tunbridge-wells": [51.1321, 0.2634],
  "winchester-on-foot": [51.0632, -1.3081],
  "box-hill-surrey": [51.2514, -0.3106]
};

/* trip type filter — visitor language */
const DT_TYPES = [
  { k: "coast", l: "Coast" },
  { k: "countryside", l: "Countryside" },
  { k: "historic", l: "Historic towns" },
  { k: "city", l: "Cities" }
];

const DIR_LABEL = {
  N: "North", NE: "North-east", E: "East", SE: "South-east",
  S: "South", SW: "South-west", W: "West", NW: "North-west"
};
/* placement around a centred London for the stylised map (percent) */
const DIR_POS = {
  N: { x: 50, y: 13 }, NE: { x: 79, y: 23 }, E: { x: 88, y: 50 }, SE: { x: 79, y: 77 },
  S: { x: 50, y: 87 }, SW: { x: 21, y: 77 }, W: { x: 12, y: 50 }, NW: { x: 21, y: 23 }
};
const LAYOUT_HINT = {
  editorial: "Big and visual, one trip at a time.",
  directory: "Compact. Scan the lot fast.",
  map: "By direction, so you know which way to head."
};

function dayTrips() {
  return (window.LFG_CONTENT || []).filter((i) => i.daytrip);
}
function tripHref(t) {
  return (t.kind === "guide" ? "Guide.html?id=" : "Spot.html?id=") + t.id;
}
function eventCount(t) {
  if (t.kind !== "guide") return 0;
  return (window.LFG_CONTENT || []).filter((i) => i.partOf === t.id).length;
}
function shortFare(f) {
  if (!f || !f.fare) return "";
  const m = f.fare.match(/£\d+/);
  return m ? "from " + m[0] : f.fare;
}
function placeName(t) {
  return (t.area || t.title).split("·")[0].trim();
}

/* destination weather strip — grouped by trip type (Coast / Countryside /
   Historic / Cities) so it mirrors the filter below, with the towns inside
   each group lightly shuffled each visit so it feels fresh */
function DestinationWeather({ trips }) {
  const [wx, setWx] = React.useState(DT_WX);
  const shuffleRef = React.useRef({});
  useDTEffect(() => {
    if (!window.LFGWeather) return;
    const list = trips.filter((t) => DT_COORDS[t.id]).map((t) => ({ id: t.id, lat: DT_COORDS[t.id][0], lon: DT_COORDS[t.id][1] }));
    if (!list.length) return;
    let live = true;
    window.LFGWeather.spots(list).then((res) => {
      if (!live || !res) return;
      const merged = Object.assign({}, DT_WX);
      Object.keys(res).forEach((id) => { merged[id] = { ic: window.LFGWeather.icon(res[id].code), t: res[id].t }; });
      setWx(merged);
    }).catch(() => {});
    return () => { live = false; };
  }, [trips]);
  // stable per-visit random key so towns shuffle once, not on every weather tick
  trips.forEach((t) => { if (shuffleRef.current[t.id] === undefined) shuffleRef.current[t.id] = Math.random(); });
  const byKey = (a, b) => shuffleRef.current[a.id] - shuffleRef.current[b.id];

  const withWx = trips.filter((t) => wx[t.id]);
  if (!withWx.length) return null;
  const groups = DT_TYPES
    .map((ty) => ({ label: ty.l, trips: withWx.filter((t) => t.type === ty.k).sort(byKey) }))
    .filter((g) => g.trips.length);
  const known = DT_TYPES.map((x) => x.k);
  const other = withWx.filter((t) => known.indexOf(t.type) === -1).sort(byKey);
  if (other.length) groups.push({ label: "More", trips: other });

  return (
    <div className="dt-wx">
      <div className="wrap dt-wx-inner">
        <span className="dt-wx-label">Out of town · today</span>
        <div className="dt-wx-row">
          {groups.map((g) => (
            <span className="dt-wx-group" key={g.label}>
              <span className="dt-wx-gl">{g.label}</span>
              {g.trips.map((t) => {
                const w = wx[t.id];
                return (
                  <a className="dt-wx-chip" key={t.id} href={tripHref(t)}>
                    <span className="dt-wx-place">{placeName(t)}</span>
                    <span className="dt-wx-ic" aria-hidden="true">{w.ic}</span>
                    <span className="dt-wx-temp">{w.t}°</span>
                  </a>
                );
              })}
            </span>
          ))}
        </div>
      </div>
    </div>
  );
}

/* the honest transport line — a feature, not fine print */
function Transit({ t, className }) {
  const f = t.fromLondon;
  if (!f) return null;
  return (
    <div className={"dt-transit" + (className ? " " + className : "")}>
      <span className="dt-train">{f.time}</span>
      <span className="dt-fare">{shortFare(f)}</span>
    </div>
  );
}

/* media frame — real cover, or a branded text-forward tile when we have no
   clean photo (Bath, Glastonbury — their only legacy images were mismatched) */
function DTMedia({ t }) {
  const dir = <span className="dt-dir-badge">{DIR_LABEL[t.dir] || t.dir}</span>;
  if (t.img) {
    return <React.Fragment><img src={t.img} alt={t.title} loading="lazy" />{dir}</React.Fragment>;
  }
  return (
    <div className="dt-noimg">
      {dir}
      <div className="dt-noimg-mid">
        <span className="dt-noimg-place">{t.area}</span>
        <span className="dt-noimg-mark">LONDON <span>FREE</span> GUIDE</span>
      </div>
    </div>
  );
}

/* ---- editorial: alternating big photo + text ---- */
function EditorialList({ trips }) {
  return (
    <div className="dt-editorial">
      {trips.map((t) => (
        <a className="dt-ed" key={t.id} href={tripHref(t)}>
          <div className={"dt-ed-media" + (t.posterCrop ? " crop" : "")}>
            <DTMedia t={t} />
          </div>
          <div className="dt-ed-body">
            <div className="dt-tagrow">
              <span className="dt-region">{t.region}</span>
              {eventCount(t) > 0 && <span className="dt-count">{eventCount(t)} free things</span>}
              <span className="dt-free">Free day out</span>
            </div>
            <h2 className="dt-ed-title">{t.title}</h2>
            <p className="dt-ed-sum">{t.summary}</p>
            <Transit t={t} />
            <span className="dt-cta">{t.kind === "guide" ? "See the weekend " : "Plan the day "}<span aria-hidden="true">→</span></span>
          </div>
        </a>
      ))}
    </div>
  );
}

/* ---- directory: compact card grid ---- */
function TripCard({ t }) {
  return (
    <a className="dt-card" href={tripHref(t)}>
      <div className={"dt-card-media" + (t.posterCrop ? " crop" : "")}>
        <DTMedia t={t} />
      </div>
      <div className="dt-card-body">
        <h2 className="dt-card-title">{t.title}</h2>
        <div className="dt-card-loc"><span className="pin" aria-hidden="true">📍</span> {t.region}{eventCount(t) > 0 ? " · " + eventCount(t) + " free things" : ""}</div>
        <p className="dt-card-sum">{t.summary}</p>
        <Transit t={t} className="dt-transit-sm" />
      </div>
    </a>
  );
}
function DirectoryList({ trips }) {
  return <div className="dt-grid">{trips.map((t) => <TripCard key={t.id} t={t} />)}</div>;
}

/* ---- map: destinations pinned by compass direction around London ---- */
function MapView({ trips }) {
  return (
    <div className="dt-mapwrap">
      <div className="dt-map">
        <div className="grid" aria-hidden="true"></div>
        <svg className="dt-lines" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
          {trips.map((t) => {
            const p = DIR_POS[t.dir] || { x: 50, y: 50 };
            return <line key={t.id} x1="50" y1="50" x2={p.x} y2={p.y} />;
          })}
        </svg>
        <div className="dt-london">
          <span className="dt-london-dot" aria-hidden="true"></span>
          <span className="dt-london-lbl">London</span>
        </div>
        {trips.map((t) => {
          const p = DIR_POS[t.dir] || { x: 50, y: 50 };
          const side = p.x > 55 ? " right" : p.x < 45 ? " left" : "";
          return (
            <a className={"dt-mappin" + side} key={t.id} href={tripHref(t)} style={{ left: p.x + "%", top: p.y + "%" }}>
              <span className="dt-tear" aria-hidden="true"></span>
              <span className="dt-pin-card">
                <b>{t.title}</b>
                <span className="dt-pin-meta">{t.region} · {t.fromLondon ? t.fromLondon.time : ""}</span>
              </span>
            </a>
          );
        })}
        <span className="dt-map-tag">Stylised · distances not to scale</span>
      </div>
      <div className="dt-grid dt-map-grid">{trips.map((t) => <TripCard key={t.id} t={t} />)}</div>
    </div>
  );
}

/* ---- trip-type filter chips ---- */
function TypeFilter({ trips, value, onChange }) {
  const counts = trips.reduce((a, t) => { if (t.type) a[t.type] = (a[t.type] || 0) + 1; return a; }, {});
  const opts = [{ k: "all", l: "All trips" }].concat(DT_TYPES.filter((o) => counts[o.k]));
  return (
    <div className="dt-filter" role="tablist" aria-label="Filter trips by type">
      {opts.map((o) => (
        <button key={o.k} role="tab" aria-selected={value === o.k}
          className={"dt-filter-chip" + (value === o.k ? " on" : "")}
          onClick={() => onChange(o.k)}>
          {o.l}{o.k !== "all" && <span className="dt-filter-n">{counts[o.k]}</span>}
        </button>
      ))}
    </div>
  );
}

/* ---- in-page layout switch (mirrors the Tweak) ---- */
function LayoutSwitch({ value, onChange }) {
  const opts = [
    { k: "editorial", l: "Editorial" },
    { k: "directory", l: "List" },
    { k: "map", l: "Map" }
  ];
  return (
    <div className="dt-switch" role="tablist" aria-label="Choose layout">
      {opts.map((o) => (
        <button key={o.k} role="tab" aria-selected={value === o.k}
          className={"dt-switch-btn" + (value === o.k ? " on" : "")}
          onClick={() => onChange(o.k)}>{o.l}</button>
      ))}
    </div>
  );
}

/* ItemList JSON-LD — public, answer-shaped, for SEO/GEO */
function DayTripsSchema({ trips }) {
  useDTEffect(() => {
    const data = {
      "@context": "https://schema.org",
      "@type": "ItemList",
      name: "Free day trips from London",
      description: "Free days out within a couple of hours of London by train.",
      url: window.location.href,
      numberOfItems: trips.length,
      itemListElement: trips.map((t, i) => ({
        "@type": "ListItem",
        position: i + 1,
        item: {
          "@type": "TouristDestination",
          name: t.title,
          url: new URL(tripHref(t), window.location.href).href,
          address: { "@type": "PostalAddress", addressLocality: t.region, addressCountry: "GB" }
        }
      }))
    };
    let el = document.getElementById("daytrips-jsonld");
    if (!el) { el = document.createElement("script"); el.type = "application/ld+json"; el.id = "daytrips-jsonld"; document.head.appendChild(el); }
    el.textContent = JSON.stringify(data, null, 2);
  }, [trips]);
  return null;
}

function DayTripsApp() {
  const [t, setTweak] = useTweaks(DT_TWEAK_DEFAULTS);
  const trips = dayTrips();
  const layout = t.layout;
  const [tripType, setTripType] = React.useState("all");
  const shown = tripType === "all" ? trips : trips.filter((x) => x.type === tripType);

  useDTEffect(() => {
    document.documentElement.style.setProperty("--lfg-accent", t.accent);
    document.body.classList.toggle("dt-hide-transit", !t.showTransport);
  }, [t]);
  useDTEffect(() => { document.title = "Free day trips from London — London Free Guide"; }, []);

  return (
    <React.Fragment>
      <DayTripsSchema trips={trips} />
      <Nav />
      {t.showWeather && <DestinationWeather trips={shown} />}
      <main>
        <section className="dt-hero">
          <div className="wrap">
            <span className="dt-hero-eyebrow">go on, leave london for the day</span>
            <h1 className="dt-hero-title">FREE DAYS OUT OF <span className="r">LONDON</span></h1>
            <p className="dt-hero-sub">Towns, coast and countryside you can do in a day. The days out are free, the trains aren't, so we show you the cheapest line and the honest fare every time.</p>
            <div className="dt-hero-meta"><span className="dt-hero-count">{trips.length} trips</span><span className="dt-hero-aside"> · more added all the time</span></div>
          </div>
        </section>

        <div className="wrap dt-controls">
          <LayoutSwitch value={layout} onChange={(v) => setTweak("layout", v)} />
          <span className="dt-controls-hint">{LAYOUT_HINT[layout]}</span>
        </div>

        <div className="wrap dt-filter-row">
          <TypeFilter trips={trips} value={tripType} onChange={setTripType} />
        </div>

        <div className="wrap dt-body">
          {layout === "editorial" && <EditorialList trips={shown} />}
          {layout === "directory" && <DirectoryList trips={shown} />}
          {layout === "map" && <MapView trips={shown} />}
        </div>

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

      <TweaksPanel>
        <TweakSection label="Day Trips layout" />
        <TweakRadio label="Page layout" value={t.layout}
          options={[{ value: "editorial", label: "Editorial" }, { value: "directory", label: "List" }, { value: "map", label: "Map" }]}
          onChange={(v) => setTweak("layout", v)} />
        <TweakToggle label="Show transport line" value={t.showTransport}
          onChange={(v) => setTweak("showTransport", v)} />
        <TweakToggle label="Show destination weather" value={t.showWeather}
          onChange={(v) => setTweak("showWeather", 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(<DayTripsApp />);
