// World Cup 2026 — Weather Operations. Host app: 16:9 stage, header (logo ·
// centered Today/Week toggle · title + live datetime + hamburger menu), and
// the data fetches. Class-based + cqw-sized (see /events.css), mirroring the
// digital-signs architecture.
//
// Data loads in two phases so we never fetch everything at once:
//   1. /api/schedule        — the skeleton (all days/matches/teams, no weather)
//   2. /api/weather?date=…   — weather for one day, fetched lazily: today first,
//      then the rest in the background, and on-demand when a day is selected.
const { useState, useEffect, useRef, useMemo, useCallback } = React;

const ACCENT = '#F05514';

function getParam(name) {
  return new URLSearchParams(window.location.search).get(name) || '';
}

function ViewToggle({ view, onChange }) {
  const tabs = [['today', 'Daily'], ['week', 'Weekly']];
  return (
    <div className="aw-events__toggle aw-events__toggle--mode">
      {tabs.map(([k, l]) => (
        <button key={k} className={`aw-events__toggle-btn${view === k ? ' is-active' : ''}`} onClick={() => onChange(k)}>{l}</button>
      ))}
    </div>
  );
}

function UnitToggle({ unit, onChange }) {
  return (
    <div className="aw-events__toggle">
      {['F', 'C'].map((u) => (
        <button key={u} className={`aw-events__toggle-btn aw-events__toggle-btn--unit${unit === u ? ' is-active' : ''}`} onClick={() => onChange(u)}>°{u}</button>
      ))}
    </div>
  );
}

function liveDateTime() {
  const now = new Date();
  try {
    const wd = new Intl.DateTimeFormat('en-US', { weekday: 'short' }).format(now);
    const md = new Intl.DateTimeFormat('en-US', { month: 'long', day: 'numeric', year: 'numeric' }).format(now);
    const tp = new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit', timeZoneName: 'short' }).formatToParts(now);
    const time = tp.filter((p) => p.type !== 'timeZoneName').map((p) => p.value).join('');
    const tz = (tp.find((p) => p.type === 'timeZoneName') || {}).value || '';
    return `${wd}, ${md} @${time} ${tz}`.trim();
  } catch (_) {
    return now.toLocaleString();
  }
}

const HamburgerIcon = (
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
    <path d="M11 17C10.4477 17 10 17.4477 10 18C10 18.5523 10.4477 19 11 19H19C19.5523 19 20 18.5523 20 18C20 17.4477 19.5523 17 19 17H11ZM5 11C4.44771 11 4 11.4477 4 12C4 12.5523 4.44771 13 5 13H19C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11H5ZM5 5C4.44771 5 4 5.44772 4 6C4 6.55228 4.44771 7 5 7H19C19.5523 7 20 6.55228 20 6C20 5.44772 19.5523 5 19 5H5Z" fill="white" />
  </svg>
);

// Hamburger → dropdown holding the °F/°C toggle. Closes on outside click / Esc.
function HamburgerMenu({ unit, setUnit }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return undefined;
    const onDown = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDown);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDown); document.removeEventListener('keydown', onKey); };
  }, [open]);
  return (
    <div className="aw-events__menu" ref={ref}>
      <button className={`aw-events__menu-btn${open ? ' is-open' : ''}`} onClick={() => setOpen((o) => !o)} aria-label="Menu" aria-expanded={open}>{HamburgerIcon}</button>
      {open && (
        <div className="aw-events__menu-panel">
          <span className="aw-events__menu-label">Temperature</span>
          <UnitToggle unit={unit} onChange={setUnit} />
        </div>
      )}
    </div>
  );
}

function Header({ unit, setUnit }) {
  const [dt, setDt] = useState(liveDateTime());
  useEffect(() => {
    const id = setInterval(() => setDt(liveDateTime()), 30000);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="aw-events__header">
      <div className="aw-events__brand">
        <img className="aw-events__brand-logo" src="/assets/logo-reversed.svg" alt="AccuWeather" />
      </div>
      <div className="aw-events__meta">
        <div className="aw-events__loc-block">
          <div className="aw-events__loc">World Cup 2026 · Weather Operations</div>
          <div className="aw-events__datetime">
            <span className="aw-events__live-dot" />
            <span><span className="aw-events__live">Live</span> · {dt}</span>
          </div>
        </div>
        <HamburgerMenu unit={unit} setUnit={setUnit} />
      </div>
    </div>
  );
}

function CenterMessage({ title, detail }) {
  return (
    <div className="aw-events__center">
      <div className="aw-events__center-title">{title}</div>
      {detail && <div className="aw-events__center-detail">{detail}</div>}
    </div>
  );
}

function App() {
  const partner = getParam('partner');
  const apikey = getParam('apikey'); // dev escape hatch (ALLOW_RAW_APIKEY)
  const authQs = useMemo(() => {
    const qs = new URLSearchParams();
    if (partner) qs.set('partner', partner);
    if (apikey) qs.set('apikey', apikey);
    return qs.toString();
  }, [partner, apikey]);

  const [unit, setUnit] = useState(() => localStorage.getItem('wc-unit') || 'F');
  const [view, setView] = useState('today');
  const [skel, setSkel] = useState({ status: 'loading' });
  const [wx, setWx] = useState({});          // matchId -> weather fields
  const [sel, setSel] = useState(0);
  const loadedDays = useRef(new Set());
  const inflight = useRef(new Set());

  useEffect(() => { localStorage.setItem('wc-unit', unit); }, [unit]);

  const loadDay = useCallback((dateKey) => {
    if (!dateKey || loadedDays.current.has(dateKey) || inflight.current.has(dateKey)) return;
    inflight.current.add(dateKey);
    fetch(`/api/weather?date=${encodeURIComponent(dateKey)}&${authQs}`)
      .then((r) => r.json())
      .then((body) => {
        inflight.current.delete(dateKey);
        if (body && body.weather) setWx((prev) => ({ ...prev, ...body.weather }));
        if (body && body.complete) loadedDays.current.add(dateKey);
        else setTimeout(() => loadDay(dateKey), 6000);
      })
      .catch(() => { inflight.current.delete(dateKey); });
  }, [authQs]);

  useEffect(() => {
    let cancelled = false;
    const timers = [];
    let skelTries = 0;
    const loadSkeleton = () => {
      fetch(`/api/schedule?${authQs}`)
        .then(async (r) => {
          const body = await r.json().catch(() => ({}));
          if (!r.ok) throw new Error(body.message || `Request failed (${r.status})`);
          return body;
        })
        .then((data) => {
          if (cancelled) return;
          window.WC = { teams: data.teams || {}, days: data.days || [], todayIndex: data.todayIndex || 0 };
          const ti = data.todayIndex || 0;
          if (skelTries === 0) setSel(ti);
          setSkel({ status: 'ready', data });
          const days = data.days || [];
          if (days.length) {
            loadDay(days[ti] && days[ti].key);
            const order = days.map((_, i) => i).filter((i) => i !== ti).sort((a, b) => Math.abs(a - ti) - Math.abs(b - ti));
            order.forEach((i, n) => { timers.push(setTimeout(() => { if (!cancelled) loadDay(days[i].key); }, 500 + n * 450)); });
          }
          skelTries += 1;
          if (!data.complete && skelTries < 6) timers.push(setTimeout(loadSkeleton, 6000));
        })
        .catch((err) => { if (!cancelled) setSkel({ status: 'error', message: err.message }); });
    };
    loadSkeleton();
    return () => { cancelled = true; timers.forEach(clearTimeout); };
  }, [authQs, loadDay]);

  const days = useMemo(() => {
    if (skel.status !== 'ready') return [];
    return skel.data.days.map((d) => ({
      ...d,
      matches: d.matches.map((m) => (wx[m.id] ? { ...m, ...wx[m.id] } : m)),
    }));
  }, [skel, wx]);

  const onSelect = useCallback((i) => {
    setSel(i);
    if (days[i]) loadDay(days[i].key);
  }, [days, loadDay]);

  const settings = { unit, accent: ACCENT };

  let body;
  if (skel.status === 'loading') {
    body = <CenterMessage title="Loading schedule…" />;
  } else if (skel.status === 'error') {
    body = <CenterMessage title="Unable to load the dashboard" detail={`${skel.message}. Append ?partner=<your token> to the URL.`} />;
  } else if (!days.length) {
    body = <CenterMessage title="No matches scheduled" />;
  } else {
    body = view === 'today'
      ? <TodayView days={days} selected={Math.min(sel, days.length - 1)} onSelect={onSelect} view={view} setView={setView} />
      : <WeekView days={days} onSelectDay={(i) => { setView('today'); onSelect(i); }} view={view} setView={setView} />;
  }

  return (
    <div className="aw-events" style={{ ['--wc-accent']: ACCENT }}>
      <WCCtx.Provider value={settings}>
        <div className="aw-events__inner">
          <Header unit={unit} setUnit={setUnit} />
          <div className="aw-events__body">{body}</div>
        </div>
      </WCCtx.Provider>
    </div>
  );
}

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