// Polished Meridian app — full interactive behaviors

const { useState, useMemo, useEffect, useRef, useCallback } = React;

// ========== Date helpers (re-export for clarity) ==========
const ISO = window.hlpISO;
const parseISO = window.hlpParseISO;
const addDays = window.hlpAddDays;
const sameDay = window.hlpSameDay;
const MONTHS = window.hlpMonths;
const DOW_SHORT = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
const MONTHS_LONG = ["January","February","March","April","May","June","July","August","September","October","November","December"];

function hostnameOf(url) {
  try { return new URL(url).hostname.replace(/^www\./, ""); }
  catch { return url; }
}

// Range of holiday data we currently ship. Anything outside is rendered as "no data yet".
const DATA_START = (() => { const d = new Date(2026, 0, 1); d.setHours(0,0,0,0); return d; })();
const DATA_END   = (() => { const d = new Date(2026, 11, 31); d.setHours(0,0,0,0); return d; })();

// ========== Region Picker ==========
function RegionPicker({ selected, onToggle, onSelectAll, onClear, onSolo, onToggleGroup }) {
  const [query, setQuery] = useState("");

  const grouped = useMemo(() => {
    const q = query.trim().toLowerCase();
    const g = {};
    for (const c of window.COUNTRIES) {
      if (q && !c.name.toLowerCase().includes(q) && !c.code.toLowerCase().includes(q)) continue;
      (g[c.region] ||= []).push(c);
    }
    return g;
  }, [query]);

  const allSelected = selected.length === window.COUNTRIES.length;

  return (
    <div className="rp">
      <div className="rp-head">
        <div className="rp-title">Regions</div>
        <div className="rp-count">{selected.length}/{window.COUNTRIES.length}</div>
      </div>

      <div className="rp-search">
        <span className="rp-search-icon">
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
            <circle cx="5" cy="5" r="3.5" stroke="currentColor" strokeWidth="1.1"/>
            <path d="M7.8 7.8L10.5 10.5" stroke="currentColor" strokeWidth="1.1" strokeLinecap="round"/>
          </svg>
        </span>
        <input
          type="text"
          placeholder="Search countries"
          value={query}
          onChange={e => setQuery(e.target.value)}
        />
        {query && <button className="rp-clear-search" onClick={() => setQuery("")}>×</button>}
      </div>

      <div className="rp-bulk">
        <button className="rp-bulk-btn" onClick={onSelectAll} disabled={allSelected}>Select all</button>
        <span className="rp-bulk-sep">·</span>
        <button className="rp-bulk-btn" onClick={onClear} disabled={selected.length === 0}>Clear</button>
      </div>

      {Object.entries(grouped).length === 0 && (
        <div className="rp-empty">No matches</div>
      )}

      {Object.entries(grouped).map(([region, list]) => {
        const onCount = list.filter(c => selected.includes(c.code)).length;
        const allOn = onCount === list.length;
        return (
        <div key={region} className="rp-group">
          <div className="rp-group-label">
            <span>{region}</span>
            <span className="rp-group-meta">{onCount}/{list.length}</span>
            <button
              className="rp-group-toggle"
              onClick={() => onToggleGroup(region)}
              title={allOn ? `Clear all ${region}` : `Select all ${region}`}
            >
              {allOn ? "Clear" : "All"}
            </button>
          </div>
          <div className="rp-items">
            {list.map(c => {
              const on = selected.includes(c.code);
              return (
                <div
                  key={c.code}
                  className={`rp-item ${on ? "is-on" : ""}`}
                  onClick={() => onToggle(c.code)}
                >
                  <span className="rp-flag">{c.flag}</span>
                  <div className="rp-text">
                    <div className="rp-name">
                      {c.name}
                      {c.src && (
                        <a
                          className="rp-src"
                          href={c.src}
                          target="_blank"
                          rel="noopener noreferrer"
                          onClick={(e) => e.stopPropagation()}
                          title={`Official source: ${hostnameOf(c.src)}`}
                        >↗</a>
                      )}
                    </div>
                    <div className="rp-meta">{c.code} · {c.tz}</div>
                  </div>
                  <span className="rp-check">{on ? "●" : "○"}</span>
                  <button
                    className="rp-solo"
                    title="Show only this region"
                    onClick={(e) => { e.stopPropagation(); onSolo(c.code); }}
                  >solo</button>
                </div>
              );
            })}
          </div>
        </div>
        );
      })}
    </div>
  );
}

// ========== Timeline (polished, with today line, click navigation, popover) ==========
function Timeline({ selectedCodes, windowDays, startDate, showWeekends, density, highlightDate, onHolidayClick, scrollToDate, scrollToRef, dataStart, dataEnd }) {
  const lookup = useMemo(window.hlpBuildLookup, []);
  const countries = useMemo(
    () => window.COUNTRIES.filter(c => selectedCodes.includes(c.code)),
    [selectedCodes]
  );
  const allDays = useMemo(() => {
    const out = []; for (let i = 0; i < windowDays; i++) out.push(addDays(startDate, i));
    return out;
  }, [startDate, windowDays]);
  const days = useMemo(
    () => showWeekends ? allDays : allDays.filter(d => d.getDay() !== 0 && d.getDay() !== 6),
    [allDays, showWeekends]
  );

  const cellW = windowDays <= 7 ? 112 : windowDays <= 14 ? 72 : windowDays <= 31 ? 44 : 22;
  const rowH = density === "compact" ? 44 : 60;

  const monthSpans = useMemo(() => {
    const spans = []; let cur = null;
    for (const d of days) {
      const key = `${d.getFullYear()}-${d.getMonth()}`;
      if (!cur || cur.key !== key) { cur = { key, month: d.getMonth(), year: d.getFullYear(), count: 1 }; spans.push(cur); }
      else cur.count++;
    }
    return spans;
  }, [days]);

  const today = useMemo(() => { const t = new Date(); t.setHours(0,0,0,0); return t; }, []);
  const todayIdx = days.findIndex(d => sameDay(d, today));

  const overlapByIdx = useMemo(
    () => days.map(d => {
      if ((dataStart && d < dataStart) || (dataEnd && d > dataEnd)) return 0;
      const iso = ISO(d); let c = 0;
      for (const code of selectedCodes) if (lookup[code]?.[iso]) c++;
      return c;
    }),
    [days, selectedCodes, lookup, dataStart, dataEnd]
  );

  // scroll-into-view for "jump to date"
  const scrollContainerRef = useRef(null);
  useEffect(() => {
    if (!scrollToDate || !scrollContainerRef.current) return;
    const idx = days.findIndex(d => sameDay(d, scrollToDate));
    if (idx >= 0) {
      const el = scrollContainerRef.current;
      const target = idx * cellW - el.clientWidth / 2 + cellW / 2 + 220; // label-w offset
      el.scrollTo({ left: target, behavior: "smooth" });
    }
  }, [scrollToDate, days, cellW]);

  // expose scroll ref
  useEffect(() => { if (scrollToRef) scrollToRef.current = scrollContainerRef.current; }, [scrollToRef]);

  if (countries.length === 0) {
    return (
      <div className="empty-state">
        <div className="empty-icon">
          <svg width="36" height="36" viewBox="0 0 36 36" fill="none">
            <circle cx="18" cy="18" r="15" stroke="currentColor" strokeWidth="1.3"/>
            <path d="M3 18H33M18 3C21 7 22 12 22 18C22 24 21 29 18 33C15 29 14 24 14 18C14 12 15 7 18 3Z" stroke="currentColor" strokeWidth="1.3"/>
          </svg>
        </div>
        <div className="empty-title">Pick at least one region</div>
        <div className="empty-sub">Select countries in the sidebar to see holidays across them</div>
      </div>
    );
  }

  return (
    <div className="ght-wrap">
      <div className="ght-scroll" ref={scrollContainerRef}>
        <div
          className={`ght-grid density-${density}`}
          style={{ "--cell-w": `${cellW}px`, "--row-h": `${rowH}px`, "--label-w": `220px` }}
        >
          <div className="ght-corner">
            <div className="ght-corner-label">Overlap</div>
            <div className="ght-corner-sub">{selectedCodes.length} region{selectedCodes.length !== 1 ? "s" : ""}</div>
          </div>

          <div className="ght-overlap-row">
            {days.map((d, i) => {
              const n = overlapByIdx[i];
              const max = selectedCodes.length || 1;
              const intensity = n / max;
              return (
                <div key={i} className="ght-overlap-cell" title={n ? `${n} of ${max} regions off` : ""}>
                  {n > 0 && (
                    <div className="ght-overlap-bar" style={{ height: `${Math.max(6, intensity * 100)}%`, opacity: 0.3 + intensity * 0.7 }}>
                      {n >= 2 && windowDays <= 31 && <span className="ght-overlap-count">{n}</span>}
                    </div>
                  )}
                </div>
              );
            })}
          </div>

          <div className="ght-month-corner" />
          <div className="ght-month-row">
            {monthSpans.map((s, i) => (
              <div key={i} className="ght-month" style={{ width: `calc(var(--cell-w) * ${s.count})` }}>
                <span className="ght-month-name">{MONTHS[s.month]}</span>
                <span className="ght-month-year">{s.year}</span>
              </div>
            ))}
          </div>

          <div className="ght-day-corner" />
          <div className="ght-day-row">
            {days.map((d, i) => {
              const isWeekend = d.getDay() === 0 || d.getDay() === 6;
              const isToday = todayIdx === i;
              return (
                <div key={i} className={`ght-day ${isWeekend ? "is-weekend" : ""} ${isToday ? "is-today" : ""}`}>
                  <div className="ght-day-num">{d.getDate()}</div>
                  {windowDays <= 31 && <div className="ght-day-dow">{DOW_SHORT[d.getDay()][0]}</div>}
                </div>
              );
            })}
          </div>

          {countries.map((c) => (
            <React.Fragment key={c.code}>
              <div className="ght-row-label">
                <div className="ght-flag">{c.flag}</div>
                <div className="ght-row-text">
                  <div className="ght-row-name">{c.name}</div>
                  <div className="ght-row-meta">{c.code} · {c.tz}</div>
                </div>
              </div>
              <div className="ght-row-cells">
                {days.map((d, i) => {
                  const iso = ISO(d);
                  const noData = (dataStart && d < dataStart) || (dataEnd && d > dataEnd);
                  const h = noData ? null : lookup[c.code]?.[iso];
                  const isWeekend = d.getDay() === 0 || d.getDay() === 6;
                  const isToday = todayIdx === i;
                  const isHighlighted = highlightDate && sameDay(d, highlightDate);
                  return (
                    <div
                      key={i}
                      className={`ght-cell ${isWeekend ? "is-weekend" : ""} ${isToday ? "is-today" : ""} ${h ? "is-holiday" : ""} ${isHighlighted ? "is-highlighted" : ""} ${noData ? "is-no-data" : ""}`}
                      onClick={(e) => h && onHolidayClick && onHolidayClick({ country: c, date: d, holiday: h, anchor: e.currentTarget })}
                      title={noData ? "Holiday data not yet available for this date" : (h ? `${h.n} — ${c.name}` : "")}
                    >
                      {h && (
                        <div className="ght-holiday">
                          <div className="ght-holiday-dot" />
                          {windowDays <= 31 && <div className="ght-holiday-name">{h.n}</div>}
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            </React.Fragment>
          ))}
        </div>
      </div>
    </div>
  );
}

// ========== Holiday popover ==========
function HolidayPopover({ data, onClose }) {
  const ref = useRef(null);
  useEffect(() => {
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); };
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    setTimeout(() => document.addEventListener("click", onDoc), 0);
    document.addEventListener("keydown", onKey);
    return () => { document.removeEventListener("click", onDoc); document.removeEventListener("keydown", onKey); };
  }, [onClose]);

  if (!data) return null;
  const { country, date, holiday, anchorRect } = data;
  const style = anchorRect
    ? { left: Math.min(anchorRect.left, window.innerWidth - 280), top: anchorRect.bottom + 8 }
    : { left: 100, top: 100 };

  return (
    <div className="popover" style={style} ref={ref}>
      <div className="popover-arrow" />
      <div className="popover-head">
        <span className="popover-flag">{country.flag}</span>
        <div>
          <div className="popover-country">{country.name}</div>
          <div className="popover-tz">{country.code} · {country.tz}</div>
        </div>
        <button className="popover-close" onClick={onClose}>×</button>
      </div>
      <div className="popover-body">
        <div className="popover-holiday">{holiday.n}</div>
        <div className="popover-date">
          {DOW_SHORT[date.getDay()]}, {MONTHS_LONG[date.getMonth()]} {date.getDate()}, {date.getFullYear()}
        </div>
        <div className="popover-tag">Nationwide public holiday</div>
        {country.src && (
          <a
            className="popover-src"
            href={country.src}
            target="_blank"
            rel="noopener noreferrer"
            title="Open the official government holiday page in a new tab"
          >
            <span className="popover-src-label">Verify on</span>
            <span className="popover-src-host">{hostnameOf(country.src)}</span>
            <span className="popover-src-arrow">↗</span>
          </a>
        )}
      </div>
    </div>
  );
}

// ========== Meeting finder ==========
function MeetingFinder({ selectedCodes, fromDate, horizon, onPickDate }) {
  const suggestions = useMemo(() => {
    const lookup = window.hlpBuildLookup();
    const results = [];
    for (let i = 0; i < horizon; i++) {
      const d = addDays(fromDate, i);
      const dow = d.getDay();
      if (dow === 0 || dow === 6) continue;
      const iso = ISO(d);
      const conflicts = [];
      for (const code of selectedCodes) {
        const h = lookup[code]?.[iso];
        if (h) { const country = window.COUNTRIES.find(c => c.code === code); conflicts.push({ country, h }); }
      }
      results.push({ date: d, iso, conflicts });
    }
    results.sort((a, b) => a.conflicts.length - b.conflicts.length || a.date - b.date);
    return results.slice(0, 4);
  }, [selectedCodes, fromDate, horizon]);

  if (selectedCodes.length === 0) return null;

  return (
    <div className="mf">
      <div className="mf-head">
        <div className="mf-title">Best days for a sync</div>
        <div className="mf-sub">Next {horizon} days · click to jump</div>
      </div>
      <div className="mf-list">
        {suggestions.map((s, i) => {
          const dow = DOW_SHORT[s.date.getDay()];
          const clean = s.conflicts.length === 0;
          return (
            <button key={i} className={`mf-item ${clean ? "is-clean" : ""}`} onClick={() => onPickDate(s.date)}>
              <div className="mf-date">
                <span className="mf-dow">{dow}</span>
                <span className="mf-dnum">{s.date.getDate()}</span>
                <span className="mf-dmon">{MONTHS[s.date.getMonth()]}</span>
              </div>
              <div className="mf-status">
                {clean
                  ? <span className="mf-clean-badge">All clear</span>
                  : (
                    <>
                      <span className="mf-conflict-count">{s.conflicts.length} conflict{s.conflicts.length > 1 ? "s" : ""}</span>
                      <div className="mf-conflict-list">
                        {s.conflicts.slice(0,3).map((c, j) => (
                          <span key={j} className="mf-conflict">
                            <span>{c.country.flag}</span>
                            <span className="mf-conflict-name">{c.h.n}</span>
                          </span>
                        ))}
                        {s.conflicts.length > 3 && (
                          <span className="mf-conflict-more">+{s.conflicts.length - 3} more</span>
                        )}
                      </div>
                    </>
                  )}
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ========== Upcoming list ==========
function UpcomingList({ selectedCodes, fromDate, days, onPickDate }) {
  const grouped = useMemo(() => {
    const lookup = window.hlpBuildLookup();
    const end = addDays(fromDate, days);
    const out = [];
    for (const code of selectedCodes) {
      const country = window.COUNTRIES.find(c => c.code === code);
      const list = window.HOLIDAYS[code] || [];
      for (const h of list) {
        const d = parseISO(h.d);
        if (d >= fromDate && d <= end) out.push({ ...h, country, dateObj: d });
      }
    }
    out.sort((a, b) => a.dateObj - b.dateObj);
    const m = new Map();
    for (const it of out) { if (!m.has(it.d)) m.set(it.d, []); m.get(it.d).push(it); }
    return Array.from(m.entries());
  }, [selectedCodes, fromDate, days]);

  const today = useMemo(() => { const t = new Date(); t.setHours(0,0,0,0); return t; }, []);

  if (selectedCodes.length === 0) return null;

  return (
    <div className="up">
      <div className="up-head">
        <div className="up-title">In view</div>
        <div className="up-sub">{days}-day window · {grouped.length} date{grouped.length !== 1 ? "s" : ""}</div>
      </div>
      <div className="up-list">
        {grouped.length === 0 && <div className="up-empty">No holidays in this window.</div>}
        {grouped.map(([date, list]) => {
          const d = parseISO(date);
          const dayOfWeek = DOW_SHORT[d.getDay()];
          const dayDiff = Math.round((d - today) / 86400000);
          return (
            <button key={date} className={`up-day ${list.length > 1 ? "is-overlap" : ""}`} onClick={() => onPickDate(d)}>
              <div className="up-date-col">
                <div className="up-date-num">{d.getDate()}</div>
                <div className="up-date-mon">{MONTHS[d.getMonth()]}</div>
                <div className="up-date-dow">{dayOfWeek}</div>
                <div className="up-date-rel">
                  {dayDiff < 0 ? `${-dayDiff}d ago` : dayDiff === 0 ? "today" : dayDiff === 1 ? "tomorrow" : `in ${dayDiff}d`}
                </div>
              </div>
              <div className="up-body">
                {list.length > 1 && (<div className="up-overlap-tag">{list.length} regions off</div>)}
                {list.map((it, i) => (
                  <div key={i} className="up-item">
                    <span className="up-flag">{it.country.flag}</span>
                    <span className="up-country">{it.country.name}</span>
                    <span className="up-sep">·</span>
                    <span className="up-name">{it.n}</span>
                  </div>
                ))}
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ========== Date nav bar ==========
const toInputValue = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;
const fromInputValue = (s) => { const [y,m,d] = s.split("-").map(Number); return new Date(y, m-1, d); };

function DateNav({ startDate, endDate, windowDays, onShift, onJumpToday, rangeLabel, isCustomRange, onApplyRange, onClearRange }) {
  const [open, setOpen] = useState(false);
  const [draftStart, setDraftStart] = useState(toInputValue(startDate));
  const [draftEnd, setDraftEnd] = useState(toInputValue(endDate));
  const popRef = useRef(null);
  const labelRef = useRef(null);

  // Reset drafts when popover opens or external dates change while closed
  useEffect(() => {
    if (open) {
      setDraftStart(toInputValue(startDate));
      setDraftEnd(toInputValue(endDate));
    }
  }, [open]); // eslint-disable-line

  // Close on outside click / Esc
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (popRef.current && !popRef.current.contains(e.target) && !labelRef.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    setTimeout(() => document.addEventListener("click", onDoc), 0);
    document.addEventListener("keydown", onKey);
    return () => { document.removeEventListener("click", onDoc); document.removeEventListener("keydown", onKey); };
  }, [open]);

  const apply = () => {
    const s = fromInputValue(draftStart);
    const e = fromInputValue(draftEnd);
    if (isNaN(s) || isNaN(e)) return;
    if (e < s) return;
    const span = Math.round((e - s) / 86400000) + 1;
    if (span > 366) return;
    onApplyRange(s, e);
    setOpen(false);
  };
  const setPreset = (days) => {
    const s = new Date(); s.setHours(0,0,0,0);
    const e = new Date(s); e.setDate(e.getDate() + days - 1);
    setDraftStart(toInputValue(s));
    setDraftEnd(toInputValue(e));
  };

  return (
    <div className="datenav">
      <button className="datenav-btn" onClick={() => onShift(-1)} title="Previous">
        <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M7.5 2.5L4 6L7.5 9.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/></svg>
      </button>
      <button className="datenav-today" onClick={onJumpToday}>Today</button>
      <button className="datenav-btn" onClick={() => onShift(1)} title="Next">
        <svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M4.5 2.5L8 6L4.5 9.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/></svg>
      </button>
      <button
        ref={labelRef}
        className={`datenav-range ${isCustomRange ? "is-custom" : ""}`}
        onClick={() => setOpen(o => !o)}
        title="Click to set a custom date range"
      >
        <span className="datenav-range-text">{rangeLabel}</span>
        <span className="datenav-range-meta">{windowDays}d</span>
        <span className="datenav-range-edit" aria-hidden="true">✎</span>
      </button>
      {isCustomRange && (
        <button className="datenav-clear" onClick={onClearRange} title="Reset to preset window">×</button>
      )}
      {open && (
        <div className="rangepop" ref={popRef}>
          <div className="rangepop-arrow" />
          <div className="rangepop-head">Set date range</div>
          <div className="rangepop-row">
            <label className="rangepop-field">
              <span className="rangepop-label">Start</span>
              <input type="date" value={draftStart} onChange={(e) => setDraftStart(e.target.value)} />
            </label>
            <label className="rangepop-field">
              <span className="rangepop-label">End</span>
              <input type="date" value={draftEnd} min={draftStart} onChange={(e) => setDraftEnd(e.target.value)} />
            </label>
          </div>
          <div className="rangepop-presets">
            <span className="rangepop-presets-label">Quick</span>
            <button onClick={() => setPreset(7)}>7d</button>
            <button onClick={() => setPreset(14)}>14d</button>
            <button onClick={() => setPreset(30)}>30d</button>
            <button onClick={() => setPreset(90)}>90d</button>
          </div>
          <div className="rangepop-foot">
            <span className="rangepop-span">
              {(() => {
                try {
                  const s = fromInputValue(draftStart), e = fromInputValue(draftEnd);
                  if (isNaN(s) || isNaN(e) || e < s) return "—";
                  const n = Math.round((e - s) / 86400000) + 1;
                  return n > 366 ? "max 366 days" : `${n} day${n !== 1 ? "s" : ""}`;
                } catch { return "—"; }
              })()}
            </span>
            <button className="rangepop-cancel" onClick={() => setOpen(false)}>Cancel</button>
            <button className="rangepop-apply" onClick={apply}>Apply</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ========== Ad slot (Ezoic placeholder) ==========
// Visible only when ?ads=preview is in the URL. In production we'll swap the
// inner placeholder div for the Ezoic placeholder ID Ezoic provides after approval.
const AD_SLOTS = {
  top:        { w: 728, h: 90,  label: "Above-fold leaderboard", note: "highest visibility" },
  "rail-mid": { w: 300, h: 100, label: "Rail mid-banner",        note: "native-feel small unit" },
  "rail-bot": { w: 300, h: 100, label: "Rail bottom-banner",     note: "low-distraction unit" },
  footer:     { w: 728, h: 90,  label: "Footer leaderboard",     note: "low impact" },
};
function AdSlot({ slot, enabled }) {
  if (!enabled) return null;
  const s = AD_SLOTS[slot];
  return (
    <div className={`ad-slot ad-slot--${slot}`} style={{ maxWidth: s.w, height: s.h }}>
      <span className="ad-slot-tag">Advertisement</span>
      <div className="ad-slot-body">
        <div className="ad-slot-title">Ezoic ad · {s.label}</div>
        <div className="ad-slot-meta">{s.w} × {s.h} · {s.note}</div>
      </div>
    </div>
  );
}
function useAdsEnabled() {
  return useMemo(() => {
    if (typeof window === "undefined") return false;
    return new URLSearchParams(window.location.search).get("ads") === "preview";
  }, []);
}

// ========== Data range banner ==========
function DataRangeBanner({ startDate, endDate, dataStart, dataEnd, onJumpToday }) {
  const beforeStart = startDate < dataStart;
  const afterEnd = endDate > dataEnd;
  const fmt = (d) => `${MONTHS_LONG[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;

  let title, message;
  if (afterEnd && !beforeStart) {
    title = "Coming soon";
    message = `Holiday data currently runs through ${fmt(dataEnd)}. We're working on the ${dataEnd.getFullYear() + 1} update — please check back soon.`;
  } else if (beforeStart && !afterEnd) {
    title = "Outside our data range";
    message = `We don't have holiday data before ${fmt(dataStart)} yet.`;
  } else {
    title = "Partially outside our data range";
    message = `Holiday data is available between ${fmt(dataStart)} and ${fmt(dataEnd)}. Other dates will be filled in soon.`;
  }

  return (
    <div className={`drb ${afterEnd ? "is-future" : "is-past"}`} role="status">
      <div className="drb-icon" aria-hidden="true">
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
          <circle cx="8" cy="8" r="6.5" stroke="currentColor" strokeWidth="1.2"/>
          <path d="M8 4.5V8L10 9.5" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </div>
      <div className="drb-text">
        <div className="drb-title">{title}</div>
        <div className="drb-message">{message}</div>
      </div>
      <button className="drb-action" onClick={onJumpToday}>Back to today</button>
    </div>
  );
}

// ========== Main app ==========
function HolidayApp({ tweaks }) {
  const adsEnabled = useAdsEnabled();
  const [selected, setSelected] = useState(["US", "GB", "DE", "IN", "JP", "AU"]);
  const [viewStart, setViewStart] = useState(null); // explicit; null means "today-aligned"
  const [customRange, setCustomRange] = useState(null); // {start: Date, end: Date}; overrides preset window when set
  const [scrollToDate, setScrollToDate] = useState(null);
  const [popover, setPopover] = useState(null);

  const windowMap = { week: 7, "2weeks": 14, month: 31, quarter: 91 };
  const presetDays = windowMap[tweaks.timeWindow] || 31;

  const today = useMemo(() => { const t = new Date(); t.setHours(0,0,0,0); return t; }, []);

  // Default start (when viewStart is null and no customRange): always start from today
  const startDate = useMemo(() => {
    if (customRange) return customRange.start;
    if (viewStart) return viewStart;
    return today;
  }, [customRange, viewStart, today]);

  const windowDays = useMemo(() => {
    if (customRange) return Math.max(1, Math.round((customRange.end - customRange.start) / 86400000) + 1);
    return presetDays;
  }, [customRange, presetDays]);

  // When user changes timeWindow, reset viewStart so it re-snaps
  useEffect(() => { setViewStart(null); }, [tweaks.timeWindow]);

  const endDate = useMemo(() => addDays(startDate, windowDays - 1), [startDate, windowDays]);

  const rangeLabel = useMemo(() => {
    const sameYear = startDate.getFullYear() === endDate.getFullYear();
    const sameMonth = sameYear && startDate.getMonth() === endDate.getMonth();
    if (sameMonth) return `${MONTHS[startDate.getMonth()]} ${startDate.getDate()}–${endDate.getDate()}, ${startDate.getFullYear()}`;
    if (sameYear) return `${MONTHS[startDate.getMonth()]} ${startDate.getDate()} – ${MONTHS[endDate.getMonth()]} ${endDate.getDate()}, ${startDate.getFullYear()}`;
    return `${MONTHS[startDate.getMonth()]} ${startDate.getDate()}, ${startDate.getFullYear()} – ${MONTHS[endDate.getMonth()]} ${endDate.getDate()}, ${endDate.getFullYear()}`;
  }, [startDate, endDate]);

  const shiftWindow = (dir) => {
    const step = Math.max(1, Math.floor(windowDays * 0.9));
    if (customRange) {
      setCustomRange({ start: addDays(customRange.start, dir * step), end: addDays(customRange.end, dir * step) });
    } else {
      setViewStart(addDays(startDate, dir * step));
    }
  };
  const jumpToday = () => { setCustomRange(null); setViewStart(null); setScrollToDate(today); setTimeout(() => setScrollToDate(null), 400); };
  const jumpTo = (d) => {
    // If the date is already inside the current window, don't change the duration —
    // just scroll to it and highlight. Only shift the window when the target is out of view.
    const inView = d >= startDate && d <= endDate;
    if (!inView) {
      if (customRange) {
        const span = windowDays;
        const newStart = addDays(d, -Math.floor(span / 3));
        setCustomRange({ start: newStart, end: addDays(newStart, span - 1) });
      } else {
        const newStart = addDays(d, -Math.floor(windowDays / 3));
        setViewStart(newStart);
      }
    }
    setScrollToDate(d);
    setTimeout(() => setScrollToDate(null), 500);
  };
  const applyRange = (s, e) => { setViewStart(null); setCustomRange({ start: s, end: e }); };
  const clearRange = () => setCustomRange(null);

  const toggle = (code) => setSelected(s => s.includes(code) ? s.filter(x => x !== code) : [...s, code]);
  const selectAll = () => setSelected(window.COUNTRIES.map(c => c.code));
  const clearAll = () => setSelected([]);
  const solo = (code) => setSelected([code]);
  const toggleGroup = (region) => {
    const codes = window.COUNTRIES.filter(c => c.region === region).map(c => c.code);
    setSelected(s => {
      const allOn = codes.every(code => s.includes(code));
      return allOn ? s.filter(code => !codes.includes(code)) : Array.from(new Set([...s, ...codes]));
    });
  };

  const handleHolidayClick = ({ country, date, holiday, anchor }) => {
    const rect = anchor.getBoundingClientRect();
    setPopover({ country, date, holiday, anchorRect: rect });
  };

  return (
    <div className="app">
      <header className="app-header">
        <div className="app-brand">
          <div className="app-logo">
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <circle cx="10" cy="10" r="8.5" stroke="currentColor" strokeWidth="1.25"/>
              <path d="M1.5 10H18.5M10 1.5C12.5 4 13.5 7 13.5 10C13.5 13 12.5 16 10 18.5C7.5 16 6.5 13 6.5 10C6.5 7 7.5 4 10 1.5Z" stroke="currentColor" strokeWidth="1.25"/>
            </svg>
          </div>
          <div className="app-brand-text">
            <div className="app-brand-name">Meridian</div>
            <div className="app-brand-sub">Global Holiday Calendar</div>
          </div>
        </div>

        <div className="app-controls">
          <div className="seg">
            {[["week","Week"],["2weeks","2 Wk"],["month","Month"],["quarter","Quarter"]].map(([v,l]) => (
              <button key={v}
                className={`seg-btn ${tweaks.timeWindow === v ? "is-on" : ""}`}
                onClick={() => { setCustomRange(null); tweaks.setTimeWindow(v); }}>{l}</button>
            ))}
          </div>
        </div>

        <div className="app-header-right">
          <div className="app-today">
            <div className="app-today-label">Today · UTC</div>
            <div className="app-today-date">
              {DOW_SHORT[today.getDay()]}, {MONTHS[today.getMonth()]} {today.getDate()}
            </div>
          </div>
        </div>
      </header>

      <AdSlot slot="top" enabled={adsEnabled} />

      <div className="app-body">
        <aside className="app-sidebar">
          <RegionPicker
            selected={selected}
            onToggle={toggle}
            onSelectAll={selectAll}
            onClear={clearAll}
            onSolo={solo}
            onToggleGroup={toggleGroup}
          />
        </aside>

        <main className="app-main">
          <DateNav
            startDate={startDate}
            endDate={endDate}
            windowDays={windowDays}
            onShift={shiftWindow}
            onJumpToday={jumpToday}
            rangeLabel={rangeLabel}
            isCustomRange={!!customRange}
            onApplyRange={applyRange}
            onClearRange={clearRange}
          />
          {(endDate > DATA_END || startDate < DATA_START) && (
            <DataRangeBanner
              startDate={startDate}
              endDate={endDate}
              dataStart={DATA_START}
              dataEnd={DATA_END}
              onJumpToday={jumpToday}
            />
          )}
          <Timeline
            selectedCodes={selected}
            windowDays={windowDays}
            startDate={startDate}
            showWeekends={tweaks.showWeekends}
            density={tweaks.density || "comfortable"}
            highlightDate={scrollToDate}
            onHolidayClick={handleHolidayClick}
            scrollToDate={scrollToDate}
            dataStart={DATA_START}
            dataEnd={DATA_END}
          />
        </main>

        <aside className="app-rail">
          <MeetingFinder selectedCodes={selected} fromDate={today} horizon={windowDays} onPickDate={jumpTo} />
          <AdSlot slot="rail-mid" enabled={adsEnabled} />
          <UpcomingList selectedCodes={selected} fromDate={startDate} days={windowDays} onPickDate={jumpTo} />
          <AdSlot slot="rail-bot" enabled={adsEnabled} />
        </aside>
      </div>

      <AdSlot slot="footer" enabled={adsEnabled} />

      <footer className="app-foot">
        <span className="app-foot-meta">© 2026 Meridian</span>
        <span className="app-foot-sep">·</span>
        <a href="/about.html">About</a>
        <a href="/sources.html">Sources</a>
        <a href="/privacy.html">Privacy</a>
        <a href="/contact.html">Contact</a>
        <span className="app-foot-spacer" />
        <span className="app-foot-meta">2026 calendar · 45 countries</span>
      </footer>

      {popover && <HolidayPopover data={popover} onClose={() => setPopover(null)} />}
    </div>
  );
}

window.HolidayApp = HolidayApp;
