// countdown.jsx — 大会カウントダウン（TOP「大会・体験会のスケジュール」の上部）
// データは components/countdown.json が唯一のマスター。
// 登録・変更は「Countdown Admin.html」または countdown-data スキル（.claude/skills/countdown-data）を参照。
(function () {
  const JST = 9 * 3600 * 1000;           // 日本時間で固定（端末のタイムゾーンに依存しない）
  const DAY = 24 * 3600 * 1000;
  const DEFAULT_LEN = 3 * 3600 * 1000;   // 終了時刻が未設定なら開始から3時間で終了扱い
  const DATA_URL = 'components/countdown.json';
  const TYPES = {
    survival:     { label: 'サバイバルバトル', icon: '⚔️', cls: 'cd-t-survival' },
    championship: { label: 'チャンピオンシップ', icon: '🏆', cls: 'cd-t-championship' },
  };

  // ===== サーバー時刻（配信サーバーの Date ヘッダーで端末の時計ずれを補正） =====
  const clock = { offset: 0, synced: false };
  function applyDateHeader(res, t0, t1) {
    const h = res && res.headers && res.headers.get('Date');
    const s = h ? Date.parse(h) : NaN;
    if (isNaN(s)) return;
    const off = s + 500 - (t0 + t1) / 2;   // Date は秒単位なので +0.5秒が中央値
    // 小さな揺れ（1秒未満）では更新しない＝表示の秒が跳ねない
    if (!clock.synced || Math.abs(off - clock.offset) >= 1000) clock.offset = off;
    clock.synced = true;
  }
  async function syncClock() {
    try {
      const t0 = Date.now();
      const res = await fetch(DATA_URL + '?sync=' + t0, { method: 'HEAD', cache: 'no-store' });
      applyDateHeader(res, t0, Date.now());
    } catch (e) { /* オフライン等は端末時刻のまま */ }
  }
  async function loadData() {
    const t0 = Date.now();
    const res = await fetch(DATA_URL + '?t=' + t0, { cache: 'no-store' });
    applyDateHeader(res, t0, Date.now());
    if (!res.ok) throw new Error('HTTP ' + res.status);
    return res.json();
  }
  const serverNow = () => Date.now() + clock.offset;

  // ===== 状態の判定（純粋関数） =====
  const toMs = v => { if (!v) return null; const n = Date.parse(v); return isNaN(n) ? null : n; };
  function normalize(e) {
    const startMs = toMs(e.start);
    if (startMs == null) return null;
    let endMs = toMs(e.end);
    if (endMs == null || endMs <= startMs) endMs = startMs + DEFAULT_LEN;
    return Object.assign({}, e, { startMs: startMs, endMs: endMs, deadlineMs: toMs(e.entryDeadline) });
  }
  function pick(data, now) {
    if (!data || data.enabled === false) return { mode: 'off' };
    const list = (data.events || [])
      .filter(e => e && e.visible !== false)
      .map(normalize)
      .filter(e => e && e.endMs > now)
      .sort((a, b) => a.startMs - b.startMs);
    if (!list.length) return { mode: 'none' };
    const ev = list[0];
    if (now >= ev.startMs) return { mode: 'live', ev: ev };
    const left = ev.startMs - now;
    const hasDeadline = ev.deadlineMs != null;
    return {
      mode: left <= DAY ? 'soon' : 'normal',
      ev: ev, left: left,
      deadlineLeft: hasDeadline && now < ev.deadlineMs ? ev.deadlineMs - now : null,
      entryOpen: !!ev.entryUrl && (!hasDeadline || now < ev.deadlineMs),
      entryClosed: !!ev.entryUrl && hasDeadline && now >= ev.deadlineMs,
    };
  }
  function split(ms) {
    const t = Math.max(0, Math.ceil(ms / 1000));
    return { d: Math.floor(t / 86400), h: Math.floor((t % 86400) / 3600), m: Math.floor((t % 3600) / 60), s: t % 60, totalH: Math.floor(t / 3600) };
  }
  function jst(ms) {
    const d = new Date(ms + JST);
    return { y: d.getUTCFullYear(), mo: d.getUTCMonth() + 1, da: d.getUTCDate(), w: '日月火水木金土'[d.getUTCDay()], hh: d.getUTCHours(), mi: d.getUTCMinutes() };
  }
  const p2 = n => (n < 10 ? '0' : '') + n;
  function fmtWhen(ev) {
    const a = jst(ev.startMs), b = jst(ev.endMs);
    const sameDay = a.y === b.y && a.mo === b.mo && a.da === b.da;
    return a.y + '年' + a.mo + '月' + a.da + '日（' + a.w + '）' + a.hh + ':' + p2(a.mi) + '〜' + (sameDay ? '' : b.mo + '/' + b.da + ' ') + b.hh + ':' + p2(b.mi);
  }
  function fmtShort(ms) { const a = jst(ms); return a.mo + '/' + a.da + '（' + a.w + '）' + a.hh + ':' + p2(a.mi); }
  function deadlineText(ms) {
    const t = split(ms);
    return ms >= 3600 * 1000 ? 'あと ' + t.totalH + '時間' : 'あと ' + Math.max(1, t.m + (t.s > 0 ? 1 : 0)) + '分';
  }

  // ===== 表示 =====
  function Tile({ value, unit, pad }) {
    return (
      <div className="cd-tile">
        <span className="cd-num">{pad ? p2(value) : value}</span>
        <span className="cd-unit">{unit}</span>
      </div>
    );
  }

  function Countdown({ data: dataProp, simOffset }) {
    const [data, setData] = React.useState(dataProp || null);
    const sim = React.useRef(simOffset || 0);
    sim.current = simOffset || 0;
    const [now, setNow] = React.useState(() => serverNow() + sim.current);

    React.useEffect(() => { if (dataProp) setData(dataProp); }, [dataProp]);
    React.useEffect(() => {
      let alive = true;
      if (dataProp) { syncClock(); return; }
      loadData().then(d => { if (alive) setData(d); }).catch(() => { if (alive) setData({ enabled: false, events: [] }); });
      return () => { alive = false; };
    }, []);
    // 秒の切れ目に合わせて毎秒更新 → 0になった瞬間に状態が切り替わる
    React.useEffect(() => {
      let id;
      const loop = () => {
        const n = serverNow() + sim.current;
        setNow(n);
        id = setTimeout(loop, 1000 - (((n % 1000) + 1000) % 1000) + 10);
      };
      loop();
      const onVis = () => { if (!document.hidden) syncClock().then(() => { clearTimeout(id); loop(); }); };
      document.addEventListener('visibilitychange', onVis);
      const resync = setInterval(syncClock, 10 * 60 * 1000);
      return () => { clearTimeout(id); clearInterval(resync); document.removeEventListener('visibilitychange', onVis); };
    }, []);

    if (!data) return null;
    const st = pick(data, now);
    if (st.mode === 'off') return null;

    if (st.mode === 'none') {
      return (
        <div className="cd cd-none" role="region" aria-label="大会カウントダウン">
          <div className="cd-kicker">NEXT TOURNAMENT</div>
          <div className="cd-headline">次回の大会は近日発表</div>
          <p className="cd-sub">お楽しみに！ 最新情報はお知らせ・公式Xでお届けします。</p>
        </div>
      );
    }

    const ev = st.ev;
    const type = TYPES[ev.type] || { label: ev.type || '大会', icon: '🎮', cls: '' };
    const meta = (
      <div className="cd-meta">
        <span>📅 {fmtWhen(ev)}</span>
        {ev.venue && <span>📍 {ev.venue}</span>}
      </div>
    );
    const buttons = (showEntry) => (
      <div className="cd-actions">
        {showEntry && <a className="cd-btn cd-btn-entry" href={ev.entryUrl} target="_blank" rel="noopener noreferrer">申し込む →</a>}
        {ev.detailUrl && <a className="cd-btn cd-btn-detail" href={ev.detailUrl} {...(/^https?:/.test(ev.detailUrl) ? { target: '_blank', rel: 'noopener noreferrer' } : {})}>詳細を見る →</a>}
      </div>
    );
    const head = (
      <div className="cd-top">
        <span className={'cd-type ' + type.cls}>{type.icon} {type.label}</span>
        <span className="cd-kicker">{st.mode === 'live' ? 'NOW ON' : 'NEXT TOURNAMENT'}</span>
      </div>
    );

    if (st.mode === 'live') {
      return (
        <div className={'cd cd-live ' + type.cls} role="region" aria-label="大会カウントダウン">
          {head}
          <h3 className="cd-name">{ev.name}</h3>
          <div className="cd-headline cd-live-text">ただいま開催中！</div>
          <p className="cd-sub">{jst(ev.endMs).hh}:{p2(jst(ev.endMs).mi)} まで開催しています。</p>
          {meta}
          {buttons(false)}
        </div>
      );
    }

    const t = split(st.left);
    const soon = st.mode === 'soon';
    return (
      <div className={'cd ' + (soon ? 'cd-soon ' : '') + type.cls} role="region" aria-label="大会カウントダウン">
        {head}
        <h3 className="cd-name">{ev.name}</h3>
        <div className="cd-headline">
          {soon ? <>まもなく開催！ <span className="cd-hl-em">あと{t.h > 0 ? t.h + '時間' : ''}{t.m}分</span></>
                : <>次の大会まで <span className="cd-hl-em">あと{t.d}日{t.h}時間</span></>}
        </div>
        <div className={'cd-tiles' + (soon ? ' cd-tiles-3' : '')} role="timer" aria-label={'開催まで ' + (soon ? '' : t.d + '日') + t.h + '時間' + t.m + '分' + t.s + '秒'}>
          {!soon && <Tile value={t.d} unit="日" />}
          <Tile value={t.h} unit="時間" pad={soon} />
          <Tile value={t.m} unit="分" pad />
          <Tile value={t.s} unit="秒" pad />
        </div>
        {meta}
        {st.deadlineLeft != null && (
          <div className="cd-deadline">📝 申込締切まで <b>{deadlineText(st.deadlineLeft)}</b><small>（{fmtShort(ev.deadlineMs)} 締切）</small></div>
        )}
        {st.entryClosed && <div className="cd-deadline cd-closed">申込受付は終了しました</div>}
        {buttons(st.entryOpen)}
      </div>
    );
  }

  Object.assign(window, { Countdown: Countdown, CountdownCore: { pick: pick, split: split, jst: jst, fmtWhen: fmtWhen, normalize: normalize, serverNow: serverNow, syncClock: syncClock, clock: clock, TYPES: TYPES } });
})();
