/* ============================================================
   WHO'S IN?  —  board-game availability for the lads
   Static React (Babel in-browser). State via window.WhosInDB.
   Threshold: 3 of 4.  Identity: player picker.  Unknown: no row.
   ============================================================ */

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

// Don't let the browser restore a previous scroll position on reload.
if ("scrollRestoration" in history) history.scrollRestoration = "manual";

/* ---------------- date helpers (all LOCAL time) ---------------- */
const pad = (n) => String(n).padStart(2, "0");
const localISO = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
const parseISO = (s) => { const [y, m, d] = s.split("-").map(Number); return new Date(y, m - 1, d); };
const addDays = (d, n) => { const x = new Date(d); x.setDate(x.getDate() + n); return x; };
const WD_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const WD_LETTER = ["S", "M", "T", "W", "T", "F", "S"];
const WD_FULL = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

const ME_KEY = "woi_me";

/* ---------------- shared-state hook ---------------- */
function useWhosIn() {
  const [state, setState] = useState(() => window.WhosInDB.getState());
  useEffect(() => window.WhosInDB.subscribe((s) => setState({ ...s })), []);
  return state;
}

function statusFor(votes, playerId, date) {
  return votes[`${playerId}|${date}`] || "unknown";
}
function tallyUps(votes, players, date) {
  return players.reduce((n, p) => n + (statusFor(votes, p.id, date) === "up" ? 1 : 0), 0);
}
function tallyDowns(votes, players, date) {
  return players.reduce((n, p) => n + (statusFor(votes, p.id, date) === "down" ? 1 : 0), 0);
}

/* ============================================================
   SHEET  — accessible modal wrapper (Escape, focus, role=dialog)
   ============================================================ */
function Sheet({ className, labelId, onClose, children }) {
  const ref = useRef(null);
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    if (ref.current) ref.current.focus();
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);
  return (
    <div className="sheet-backdrop" onClick={onClose}>
      <div
        ref={ref}
        className={"sheet " + className}
        role="dialog"
        aria-modal="true"
        aria-labelledby={labelId}
        tabIndex={-1}
        onClick={(e) => e.stopPropagation()}
      >
        {children}
      </div>
    </div>
  );
}

/* ============================================================
   PLAYER PICKER  — "who are you?"
   ============================================================ */
function PlayerPicker({ players, me, onPick, onClose }) {
  return (
    <Sheet className="picker" labelId="picker-title" onClose={onClose}>
      <h2 className="picker__title" id="picker-title">Who are you?</h2>
      <p className="picker__sub">So we know whose thumb is whose.</p>
      <div className="picker__grid">
        {players.map((p) => (
          <button
            key={p.id}
            className={"picker__chip" + (me === p.id ? " is-me" : "")}
            style={{ "--c": p.color }}
            onClick={() => onPick(p.id)}
          >
            <span className="picker__dot" />
            <span className="picker__name">{p.name}</span>
          </button>
        ))}
      </div>
      {onClose && <button className="sheet__close" onClick={onClose}>Done</button>}
    </Sheet>
  );
}

/* ============================================================
   TIMELINE — vertical-filling day carousel
   ============================================================ */
function DayCard({ date, isToday, active, players, votes, threshold, onOpen, onFocus, registerRef }) {
  const d = parseISO(date);
  const ups = tallyUps(votes, players, date);
  const downs = tallyDowns(votes, players, date);
  const gameOn = ups >= threshold;
  const cls = ["day"];
  if (isToday) cls.push("is-today");
  if (active) cls.push("is-active");
  if (gameOn) cls.push("is-on");
  // Tapping the focused card opens the vote editor. Tapping a peeking
  // neighbor just brings it into focus — no editor, matches "swipe" intent.
  const handleClick = () => { if (active) onOpen(date); else onFocus(date); };
  return (
    <button ref={registerRef} className={cls.join(" ")} onClick={handleClick}>
      <div className="day__head">
        <span className="day__mon">{MONTHS[d.getMonth()]}</span>
        <span className="day__num">{d.getDate()}</span>
        <span className="day__wd">{WD_FULL[d.getDay()]}</span>
        {isToday && <span className="day__today">Today</span>}
      </div>

      <ul className="day__players">
        {players.map((p) => {
          const s = statusFor(votes, p.id, date);
          return (
            <li key={p.id} className={"dp dp--" + s} style={{ "--c": p.color }}>
              <span className="dp__dot" />
              <span className="dp__name">{p.name}</span>
              <span className="dp__status">{s === "up" ? "in" : s === "down" ? "out" : "—"}</span>
            </li>
          );
        })}
      </ul>

      <div className={"day__foot" + (gameOn ? " is-on" : "")}>
        {gameOn
          ? <span><span className="day__foot-star">✱</span> Game night is ON · {ups} in</span>
          : ups > 0
          ? <span>{ups} in · {Math.max(0, threshold - ups)} more for game night</span>
          : downs > 0
          ? <span>nobody's in yet</span>
          : <span className="day__quiet">no word yet — tap to vote</span>}
      </div>
    </button>
  );
}

function Timeline({ dates, todayKey, players, votes, threshold, onOpen }) {
  const scrollRef = useRef(null);
  const todayRef = useRef(null);
  const cellRefs = useRef({});
  const [activeKey, setActiveKey] = useState(todayKey);

  const scrollToDate = useCallback((date, smooth) => {
    const c = scrollRef.current, t = cellRefs.current[date];
    if (!c || !t) return;
    // Use viewport rects so the app's centering margin cancels out (offsetLeft
    // is relative to <body>, which broke centering on wide screens).
    const cRect = c.getBoundingClientRect();
    const tRect = t.getBoundingClientRect();
    const cellLeftInContent = (tRect.left - cRect.left) + c.scrollLeft;
    const target = Math.max(0, cellLeftInContent + tRect.width / 2 - c.clientWidth / 2);
    if (smooth && c.scrollTo) c.scrollTo({ left: target, behavior: "smooth" });
    else c.scrollLeft = target;
    setActiveKey(date);
  }, []);

  const scrollToToday = useCallback((smooth) => scrollToDate(todayKey, smooth), [scrollToDate, todayKey]);

  // Center today on mount; re-assert to beat browser scroll-position restoration.
  useEffect(() => {
    const go = () => scrollToToday(false);
    const id = requestAnimationFrame(go);
    const timers = [80, 250, 500].map((ms) => setTimeout(go, ms));
    window.addEventListener("pageshow", go);
    return () => {
      cancelAnimationFrame(id);
      timers.forEach(clearTimeout);
      window.removeEventListener("pageshow", go);
    };
  }, [scrollToToday]);

  // Track which card is centered (the carousel focus).
  useEffect(() => {
    const c = scrollRef.current;
    if (!c) return;
    let raf = 0;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        raf = 0;
        const cRect = c.getBoundingClientRect();
        const mid = cRect.left + cRect.width / 2;
        let best = null, bestDist = Infinity;
        for (const k in cellRefs.current) {
          const el = cellRefs.current[k];
          if (!el) continue;
          const r = el.getBoundingClientRect();
          const dist = Math.abs(r.left + r.width / 2 - mid);
          if (dist < bestDist) { bestDist = dist; best = k; }
        }
        if (best) setActiveKey(best);
      });
    };
    c.addEventListener("scroll", onScroll, { passive: true });
    return () => c.removeEventListener("scroll", onScroll);
  }, []);

  const onToday = activeKey === todayKey;
  return (
    <div className="timeline">
      <div className="timeline__rail" ref={scrollRef}>
        {dates.map((date) => (
          <DayCard
            key={date}
            date={date}
            isToday={date === todayKey}
            active={date === activeKey}
            registerRef={(el) => {
              cellRefs.current[date] = el;
              if (date === todayKey) todayRef.current = el;
            }}
            players={players}
            votes={votes}
            threshold={threshold}
            onOpen={onOpen}
            onFocus={(d) => scrollToDate(d, true)}
          />
        ))}
      </div>
      <button
        className={"todaybtn" + (onToday ? " is-here" : "")}
        onClick={() => scrollToToday(true)}
        aria-label="jump to today"
      >
        <span className="todaybtn__dot" />
        {onToday ? "Today" : "Jump to today"}
      </button>
    </div>
  );
}

/* ============================================================
   DAY DETAIL — four-up player grid
   ============================================================ */
function PlayerQuadrant({ player, status, isMe, onVote, onClaim }) {
  const cls = ["quad", "quad--" + status];
  if (isMe) cls.push("is-me");
  return (
    <div className={cls.join(" ")} style={{ "--c": player.color }}>
      <div className="quad__head">
        <span className="quad__name">{player.name}</span>
        {isMe && <span className="quad__you">you</span>}
      </div>

      <div className="quad__face">
        {status === "up" && <span className="quad__glyph">👍</span>}
        {status === "down" && <span className="quad__glyph">👎</span>}
        {status === "unknown" && <span className="quad__glyph quad__glyph--q">—</span>}
        <span className="quad__label">
          {status === "up" ? "In" : status === "down" ? "Out" : "No word yet"}
        </span>
      </div>

      {isMe ? (
        <div className="quad__controls">
          <button
            className={"vote vote--up" + (status === "up" ? " active" : "")}
            onClick={() => onVote(status === "up" ? null : "up")}
            aria-label="thumbs up"
          >👍</button>
          <button
            className={"vote vote--down" + (status === "down" ? " active" : "")}
            onClick={() => onVote(status === "down" ? null : "down")}
            aria-label="thumbs down"
          >👎</button>
        </div>
      ) : onClaim ? (
        <button className="quad__claim" onClick={onClaim}>tap if this is you</button>
      ) : null}
    </div>
  );
}

function DayDetail({ date, players, votes, threshold, me, onClose, onPickMe }) {
  const d = parseISO(date);
  const ups = tallyUps(votes, players, date);
  const gameOn = ups >= threshold;
  const needed = Math.max(0, threshold - ups);

  return (
    <Sheet className="day" labelId="day-title" onClose={onClose}>
      <div className="day__head">
        <div>
          <div className="day__wd" id="day-title">{WD_FULL[d.getDay()]}</div>
          <div className="day__date">{MONTHS[d.getMonth()]} {d.getDate()}</div>
        </div>
        <button className="day__x" onClick={onClose} aria-label="close">✕</button>
      </div>

      <div className={"day__status" + (gameOn ? " is-on" : "")}>
        {gameOn ? (
          <><span className="day__status-star">✱</span> Game night is <strong>ON</strong> — {ups} in</>
        ) : (
          <>{ups} in · <span className="day__need">{needed} more for game night</span></>
        )}
      </div>

      <div className="quad-grid">
        {players.map((p) => (
          <PlayerQuadrant
            key={p.id}
            player={p}
            status={statusFor(votes, p.id, date)}
            isMe={me === p.id}
            onVote={(s) => window.WhosInDB.setVote(p.id, date, s)}
            onClaim={!me ? () => onPickMe(p.id) : null}
          />
        ))}
      </div>

      {!me && <p className="day__hint">Tap your square to claim it, then vote.</p>}
    </Sheet>
  );
}

/* ============================================================
   APP
   ============================================================ */
function App() {
  const state = useWhosIn();
  const { group, players, votes, loading } = state;
  const threshold = (group && group.threshold) || 3;

  const [me, setMe] = useState(() => localStorage.getItem(ME_KEY) || null);
  const [openDate, setOpenDate] = useState(null);
  const [pickerOpen, setPickerOpen] = useState(false);

  const pickMe = useCallback((id) => {
    setMe(id);
    localStorage.setItem(ME_KEY, id);
    setPickerOpen(false);
  }, []);

  const dates = useMemo(() => {
    // A week of past for context; today is centered on load (see Timeline scroll).
    const today = new Date(); today.setHours(0, 0, 0, 0);
    const start = addDays(today, -7);
    return Array.from({ length: 49 }, (_, i) => localISO(addDays(start, i)));
  }, []);
  const todayKey = useMemo(() => localISO(new Date()), []);

  const mePlayer = players.find((p) => p.id === me);

  return (
    <div className="app">
      <header className="topbar">
        <div className="topbar__brand">
          <a className="topbar__home" href="../" aria-label="back to hub">‹</a>
          <div>
            <div className="topbar__kicker">there's always an *</div>
            <h1 className="topbar__title">Who's In?</h1>
          </div>
        </div>
        <button
          className={"mechip" + (mePlayer ? "" : " mechip--empty")}
          style={mePlayer ? { "--c": mePlayer.color } : undefined}
          onClick={() => setPickerOpen(true)}
        >
          {mePlayer ? (
            <><span className="mechip__dot" /><span className="mechip__name">{mePlayer.name}</span></>
          ) : "Pick you"}
        </button>
      </header>

      <p className="lede">
        Throw your thumb in for any night. <strong>{threshold} thumbs-up</strong> and game night's on.
      </p>

      {state.error && (
        <div className="errbar" role="alert">
          <span>Couldn't save your vote — check your connection.</span>
          <button onClick={() => window.WhosInDB.clearError()} aria-label="dismiss">✕</button>
        </div>
      )}

      {loading ? (
        <div className="loading">loading the calendar…</div>
      ) : (
        <Timeline
          dates={dates}
          todayKey={todayKey}
          players={players}
          votes={votes}
          threshold={threshold}
          onOpen={setOpenDate}
        />
      )}

      {openDate && (
        <DayDetail
          date={openDate}
          players={players}
          votes={votes}
          threshold={threshold}
          me={me}
          onClose={() => setOpenDate(null)}
          onPickMe={pickMe}
        />
      )}

      {pickerOpen && (
        <PlayerPicker
          players={players}
          me={me}
          onPick={pickMe}
          onClose={() => setPickerOpen(false)}
        />
      )}
    </div>
  );
}

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