/* iBD Coliseum — Competitions: when a Season runs, and when the Arena opens
 * and closes.
 *
 * Seasons were calendar quarters worked out in the browser, which meant a
 * season could only ever be a quarter and only I could change that. They are
 * rows here: a label and three dates, set by the people running the thing.
 *
 * Two rules the editor enforces, because both fail silently otherwise. A gap
 * between seasons leaves any card shared in it with no season at all — no
 * points bucket, missing from the Library's filter. An overlap makes the
 * lookup pick whichever season started later, which is a coin toss rather
 * than a decision.
 */

function seasonIssues(rows) {
  const issues = [];
  const breaks = [];
  const sorted = [...rows].filter((r) => r.startsOn && r.endsOn)
    .sort((a, b) => a.startsOn.localeCompare(b.startsOn));

  sorted.forEach((r) => {
    if (r.endsOn < r.startsOn) issues.push({ id: r.id, text: `${r.label}: ends before it starts.` });
    if (r.championsOn && (r.championsOn < r.startsOn || r.championsOn > r.endsOn)) {
      issues.push({ id: r.id, text: `${r.label}: the championship day falls outside the season.` });
    }
  });

  for (let i = 1; i < sorted.length; i++) {
    const prev = sorted[i - 1], cur = sorted[i];
    const dayAfter = (iso) => {
      const d = new Date(iso + "T12:00:00");
      d.setDate(d.getDate() + 1);
      return d.toISOString().slice(0, 10);
    };
    if (cur.startsOn <= prev.endsOn) {
      issues.push({ id: cur.id, text: `${cur.label} overlaps ${prev.label}. A day in two seasons lands in whichever started later.` });
    } else if (cur.startsOn !== dayAfter(prev.endsOn)) {
      // A break is allowed — the Coliseum simply closes for it — so this is
      // told, not blocked. Only overlaps and impossible dates stop a save.
      breaks.push({ from: dayAfter(prev.endsOn), to: cur.startsOn, after: prev.label, before: cur.label });
    }
  }
  issues.breaks = breaks;
  return issues;
}

function SeasonRow({ row, issues, saving, saved, onChange, onSave, onRemove, isCurrent }) {
  const dirty = row.dirty;
  const bad = issues.some((i) => i.id === row.id);
  return (
    <div className={"cmp-row" + (isCurrent ? " now" : "") + (bad ? " bad" : "")}>
      <div className="cmp-cell cmp-label">
        <label>Season</label>
        <input type="text" value={row.label} placeholder="2027 Season 1"
          onChange={(e) => onChange(row.id, "label", e.target.value)} />
        {isCurrent && <span className="cmp-now">Running now</span>}
      </div>
      <div className="cmp-cell">
        <label>Opens</label>
        <input type="date" value={row.startsOn} onChange={(e) => onChange(row.id, "startsOn", e.target.value)} />
      </div>
      <div className="cmp-cell">
        <label>Closes</label>
        <input type="date" value={row.endsOn} onChange={(e) => onChange(row.id, "endsOn", e.target.value)} />
      </div>
      <div className="cmp-cell">
        <label>Champion crowned</label>
        <input type="date" value={row.championsOn || ""}
          onChange={(e) => onChange(row.id, "championsOn", e.target.value)} />
      </div>
      <div className="cmp-cell cmp-acts">
        <button className="tm-save" disabled={!dirty || !!saving || bad} onClick={() => onSave(row)}>
          {saving ? "Saving…" : saved ? "Saved" : "Save"}
        </button>
        {onRemove && !isCurrent &&
          <button className="cmp-del" onClick={() => onRemove(row)} title="Remove this season">Remove</button>}
      </div>
    </div>
  );
}

function Competitions({ seasons, arenaDates, onSaveSeason, onAddSeason, onRemoveSeason, onSaveArena, today }) {
  const D = window.COLISEUM_DATA;
  const [rows, setRows] = React.useState(seasons);
  const [saving, setSaving] = React.useState({});
  const [saved, setSaved] = React.useState({});
  const [error, setError] = React.useState("");

  React.useEffect(() => { setRows(seasons); }, [seasons]);

  const issues = seasonIssues(rows);
  const now = today || D.ymd(D.nowDate());
  const currentId = (rows.find((r) => r.startsOn <= now && now <= r.endsOn) || {}).id;

  const change = (id, field, value) =>
    setRows((rs) => rs.map((r) => (r.id === id ? { ...r, [field]: value, dirty: true } : r)));

  const save = async (row) => {
    setError("");
    setSaving((s) => ({ ...s, [row.id]: true }));
    try {
      await onSaveSeason(row);
      setRows((rs) => rs.map((r) => (r.id === row.id ? { ...r, dirty: false } : r)));
      setSaved((s) => ({ ...s, [row.id]: true }));
      setTimeout(() => setSaved((s) => { const n = { ...s }; delete n[row.id]; return n; }), 2000);
    } catch (err) {
      setError(err.message || "Could not save that season.");
    } finally {
      setSaving((s) => { const n = { ...s }; delete n[row.id]; return n; });
    }
  };

  // Arena
  const [arena, setArena] = React.useState(arenaDates);
  const [arenaSaving, setArenaSaving] = React.useState("");
  const [arenaSaved, setArenaSaved] = React.useState("");
  React.useEffect(() => { setArena(arenaDates); }, [arenaDates]);
  // The three dates have to run in order, or a state is unreachable: voting
  // that closes before submissions do, or an Arena that opens after it shuts.
  const arenaOrderWrong =
    (arena.opens && arena.submit && arena.submit < arena.opens)
    || (arena.submit && arena.vote && arena.vote < arena.submit);
  const arenaState = arena.opens && now < arena.opens
    ? { label: `Not open yet — opens ${arena.opens}`, tone: "soon" }
    : now <= arena.submit
      ? { label: "Submissions open · voting open", tone: "ok" }
      : now <= arena.vote
        ? { label: "Submissions closed · voting open", tone: "warn" }
        : { label: "Closed — entries hidden from everyone but execs", tone: "shut" };

  const saveArena = async (key, value) => {
    setError("");
    setArenaSaving(key);
    try {
      await onSaveArena(key, value);
      setArenaSaved(key);
      setTimeout(() => setArenaSaved(""), 2000);
    } catch (err) {
      setError(err.message || "Could not save that date.");
    } finally { setArenaSaving(""); }
  };

  return (
    <div className="cmp-page">
      <section className="ghero arena">
        <div className="wrap">
          <div className="eyebrow">iBD Coliseum · Competitions</div>
          <h1>When things <em>run</em></h1>
          <p className="sub">
            The Seasons people earn points in, and the dates that open and close the Arena.
            Changes apply to everyone on their next page load.
          </p>
        </div>
      </section>

      <div className="wrap cmp-body">
        {error && <p className="pe-save-error" role="alert">{error}</p>}

        <div className="dcard">
          <div className="dh">
            <h3>The Arena</h3>
            <span className="sub">Quest submissions and voting</span>
          </div>
          {arenaOrderWrong &&
            <p className="ss-warn" role="alert">The dates must run in order: opens, then submissions close, then voting closes.</p>}
          <div className="ss-rows">
            <div className="ss-row">
              <label className="ss-label" htmlFor="cmp-opens">Arena opens</label>
              <input id="cmp-opens" className="ss-input" type="date" value={arena.opens || ""}
                onChange={(e) => setArena((a) => ({ ...a, opens: e.target.value }))} />
              <button className="tm-save" disabled={arena.opens === arenaDates.opens || !!arenaSaving || arenaOrderWrong}
                onClick={() => saveArena("arena_opens_on", arena.opens)}>
                {arenaSaving === "arena_opens_on" ? "Saving…" : arenaSaved === "arena_opens_on" ? "Saved" : "Save"}
              </button>
            </div>
            <div className="ss-row">
              <label className="ss-label" htmlFor="cmp-submit">Submissions close</label>
              <input id="cmp-submit" className="ss-input" type="date" value={arena.submit}
                onChange={(e) => setArena((a) => ({ ...a, submit: e.target.value }))} />
              <button className="tm-save" disabled={arena.submit === arenaDates.submit || !!arenaSaving || arenaOrderWrong}
                onClick={() => saveArena("submit_closes_on", arena.submit)}>
                {arenaSaving === "submit_closes_on" ? "Saving…" : arenaSaved === "submit_closes_on" ? "Saved" : "Save"}
              </button>
            </div>
            <div className="ss-row">
              <label className="ss-label" htmlFor="cmp-vote">Voting closes</label>
              <input id="cmp-vote" className="ss-input" type="date" value={arena.vote}
                onChange={(e) => setArena((a) => ({ ...a, vote: e.target.value }))} />
              <button className="tm-save" disabled={arena.vote === arenaDates.vote || !!arenaSaving || arenaOrderWrong}
                onClick={() => saveArena("vote_closes_on", arena.vote)}>
                {arenaSaving === "vote_closes_on" ? "Saving…" : arenaSaved === "vote_closes_on" ? "Saved" : "Save"}
              </button>
            </div>
          </div>
          {/* Read from the draft values, so the consequence is visible before
              Save rather than discovered after it. */}
          <div className={"ss-preview " + arenaState.tone}>
            <span className="ss-preview-label">Right now the Arena shows</span>
            <span className="ss-preview-value">{arenaState.label}</span>
          </div>
        </div>

        <div className="dcard">
          <div className="dh">
            <h3>Seasons</h3>
            <span className="sub">{rows.length} {rows.length === 1 ? "season" : "seasons"} · points reset when one ends</span>
          </div>

          {issues.length > 0 &&
            <div className="cmp-issues" role="alert">
              <b>{issues.length} {issues.length === 1 ? "problem" : "problems"} to fix before these can be saved</b>
              <ul>{issues.map((i, n) => <li key={n}>{i.text}</li>)}</ul>
            </div>}

          {/* Deliberate breaks are stated, not flagged. The Coliseum closes
              for them: nobody can share a card, because it would belong to no
              season and earn nobody anything. */}
          {(issues.breaks || []).length > 0 &&
            <div className="cmp-breaks">
              <b>The Coliseum is closed between these seasons</b>
              <ul>
                {issues.breaks.map((b, n) =>
                  <li key={n}>{b.from} to the day before {b.to} — after {b.after}, before {b.before}. Sharing is paused and the Season page says so.</li>)}
              </ul>
            </div>}

          <div className="cmp-rows">
            {rows.slice().sort((a, b) => String(a.startsOn).localeCompare(String(b.startsOn))).map((r) =>
              <SeasonRow key={r.id} row={r} issues={issues} saving={saving[r.id]} saved={saved[r.id]}
                isCurrent={r.id === currentId}
                onChange={change} onSave={save} onRemove={onRemoveSeason} />
            )}
          </div>

          {onAddSeason &&
            <button className="quest-btn ghost sm cmp-add" onClick={onAddSeason}>+ Add a season</button>}

          <p className="tm-note">
            A card is filed under whichever Season covers the day it was shared, so the dates must
            run end to end with no gap. The champion is crowned on the day you set, which can be
            before the Season closes.
          </p>
        </div>
      </div>
    </div>
  );
}

window.Competitions = Competitions;
window.seasonIssues = seasonIssues;
