// =====================================================================
// 生態系ウォーズ — トーナメント アプリ本体
// 登録 → 組み合わせ抽選（ガチャ演出）→ 勝ち上がり → 優勝演出
// =====================================================================

const TN_KEY = 'ew_team_tournament_v2';
const TN_MIN = 4;
const TN_MAX = 30;
const TN_COLORS = [
  '#FF7A1A', '#4FA3D1', '#6FB76A', '#B279C7',
  '#E6421A', '#FFC21A', '#1A6FBF', '#1E6B4F',
  '#FF5A4E', '#8E5BB4', '#4FB58B', '#C97B3A',
  '#6F8FA8', '#D9568C', '#3FA796', '#E0A92E',
  '#2E8B57', '#D97706', '#7C5CBF', '#0E7C86',
  '#C2410C', '#2563C4', '#A1894B', '#B0457B',
  '#5A8F3C', '#CA5A2E', '#4C6FB0', '#9C6B2E',
  '#3FA0A0', '#B5483F',
];

/* ---------- helpers ---------- */
function tnLoad() {
  try { return JSON.parse(localStorage.getItem(TN_KEY)) || null; } catch (e) { return null; }
}
function tnSave(s) {
  try { if (s) localStorage.setItem(TN_KEY, JSON.stringify(s)); else localStorage.removeItem(TN_KEY); } catch (e) {}
}
function tnNextPow2(n) { let p = 1; while (p < n) p *= 2; return p; }
function tnShuffle(arr) {
  const a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}
// standard seeding: slot order -> seed number (1-indexed), length = size (power of 2)
function tnSeedSlots(size) {
  let seeds = [1, 2];
  while (seeds.length < size) {
    const sum = seeds.length * 2 + 1;
    const next = [];
    seeds.forEach(s => { next.push(s); next.push(sum - s); });
    seeds = next;
  }
  return seeds;
}
function tnDrawSlots(teams, bracketSize) {
  const seeds = tnSeedSlots(bracketSize);
  const order = tnShuffle(teams.map(t => t.id));
  return seeds.map(s => (s <= teams.length ? order[s - 1] : 'BYE'));
}

/* ---------- build bracket rounds from slots + results ---------- */
function tnBuildRounds(slots, bracketSize, results) {
  const totalRounds = Math.round(Math.log2(bracketSize));
  const rounds = [];
  let prevWinners = null;
  for (let r = 0; r < totalRounds; r++) {
    const numMatches = bracketSize / Math.pow(2, r + 1);
    const matches = [];
    for (let i = 0; i < numMatches; i++) {
      let aSrc, bSrc;
      if (r === 0) { aSrc = slots[2 * i]; bSrc = slots[2 * i + 1]; }
      else { aSrc = prevWinners[2 * i]; bSrc = prevWinners[2 * i + 1]; }
      const aPart = tnPart(aSrc, r);
      const bPart = tnPart(bSrc, r);
      const id = `r${r}m${i}`;
      const hasBye = aPart.kind === 'bye' || bPart.kind === 'bye';
      const bothKnown = aPart.kind === 'team' && bPart.kind === 'team';
      let winner = null;
      if (hasBye) {
        if (aPart.kind === 'team') winner = aPart.teamId;
        else if (bPart.kind === 'team') winner = bPart.teamId;
      } else if (results[id] != null && (results[id] === aPart.teamId || results[id] === bPart.teamId)) {
        winner = results[id];
      }
      matches.push({ id, a: aPart, b: bPart, winner, hasBye, bothKnown });
    }
    rounds.push(matches);
    prevWinners = matches.map(m => m.winner);
  }
  return rounds;
}
function tnPart(src, round) {
  if (round === 0) {
    if (src === 'BYE') return { kind: 'bye' };
    if (src == null) return { kind: 'tbd' };
    return { kind: 'team', teamId: src };
  }
  // later rounds: src is a winner teamId or null
  if (src == null) return { kind: 'tbd' };
  return { kind: 'team', teamId: src };
}
// prune stored results that reference teams no longer present in their match
function tnPrune(slots, bracketSize, results) {
  let r = { ...results };
  for (let pass = 0; pass < 5; pass++) {
    const rounds = tnBuildRounds(slots, bracketSize, r);
    let changed = false;
    rounds.forEach(matches => matches.forEach(m => {
      if (r[m.id] != null && m.winner !== r[m.id] && !m.hasBye) {
        // stored winner is not a valid participant anymore
        const valid = (m.a.kind === 'team' && m.a.teamId === r[m.id]) ||
                      (m.b.kind === 'team' && m.b.teamId === r[m.id]);
        if (!valid) { delete r[m.id]; changed = true; }
      }
    }));
    if (!changed) break;
  }
  return r;
}

/* ---------- ranking (placement by elimination round) ---------- */
function tnRanking(rounds, teams) {
  const totalRounds = rounds.length;
  const bracketSize = Math.pow(2, totalRounds);
  const exitRound = {};
  const wins = {};
  const deepest = {};
  rounds.forEach((matches, r) => {
    matches.forEach(m => {
      if (m.winner != null && m.bothKnown) {
        const loserId = (m.a.teamId === m.winner) ? m.b.teamId : m.a.teamId;
        exitRound[loserId] = r;
        wins[m.winner] = (wins[m.winner] || 0) + 1;
      }
      [m.a, m.b].forEach(p => {
        if (p.kind === 'team') deepest[p.teamId] = Math.max(deepest[p.teamId] || 0, r);
      });
    });
  });
  const finalMatch = rounds[totalRounds - 1][0];
  const champId = finalMatch.winner != null ? finalMatch.winner : null;
  const roundMeta = TN_ROUND_META(totalRounds);

  const entries = teams.map(t => {
    if (t.id === champId) {
      return { team: t, rank: 1, label: '優勝', tier: 'champion', alive: false, wins: wins[t.id] || 0 };
    }
    if (exitRound[t.id] != null) {
      const r = exitRound[t.id];
      const rank = Math.pow(2, totalRounds - 1 - r) + 1;
      const bestN = bracketSize / Math.pow(2, r);
      const label = rank === 2 ? '準優勝' : `ベスト${bestN}`;
      return { team: t, rank, label, tier: 'out', alive: false, wins: wins[t.id] || 0 };
    }
    const d = deepest[t.id] || 0;
    return { team: t, rank: null, label: '勝ち残り', tier: 'alive', alive: true,
      wins: wins[t.id] || 0, stage: roundMeta(d).name };
  });

  // count ties per rank
  const counts = {};
  entries.forEach(e => { if (e.rank != null) counts[e.rank] = (counts[e.rank] || 0) + 1; });
  entries.forEach(e => { if (e.rank != null) e.tie = counts[e.rank] > 1; });

  entries.sort((a, b) => {
    if (a.alive && b.alive) return (b.wins - a.wins) || (a.team.id - b.team.id);
    if (a.alive) return -1;
    if (b.alive) return 1;
    if (a.rank !== b.rank) return a.rank - b.rank;
    return (b.wins - a.wins) || (a.team.id - b.team.id);
  });
  return { entries, complete: champId != null };
}

/* ===================================================================
   APP
   =================================================================== */
function TnApp() {
  const [state, setState] = React.useState(() => tnLoad());
  const [view, setView] = React.useState('bracket');
  const [draw, setDraw] = React.useState({ open: false, spinning: false, pair: null, label: '' });
  const [recentWin, setRecentWin] = React.useState(null);
  const [champOpen, setChampOpen] = React.useState(false);
  const timersRef = React.useRef([]);
  const prevChampRef = React.useRef(null);

  React.useEffect(() => { tnSave(state); }, [state]);

  const teamsById = React.useMemo(() => {
    const m = {};
    if (state && state.teams) state.teams.forEach(t => { m[t.id] = t; });
    return m;
  }, [state]);

  const rounds = React.useMemo(() => {
    if (!state || state.phase !== 'running' || !state.slots) return null;
    return tnBuildRounds(state.slots, state.bracketSize, state.results || {});
  }, [state]);

  const champion = React.useMemo(() => {
    if (!rounds) return null;
    const last = rounds[rounds.length - 1][0];
    return last && last.winner != null ? teamsById[last.winner] : null;
  }, [rounds, teamsById]);

  const ranking = React.useMemo(() => {
    if (!rounds) return null;
    return tnRanking(rounds, state.teams);
  }, [rounds, state]);

  // trigger champion celebration when newly decided
  React.useEffect(() => {
    const id = champion ? champion.id : null;
    if (id != null && prevChampRef.current == null) setChampOpen(true);
    prevChampRef.current = id;
  }, [champion]);

  const clearTimers = () => { timersRef.current.forEach(clearTimeout); timersRef.current = []; };
  React.useEffect(() => () => clearTimers(), []);

  /* ---- actions ---- */
  const confirmTeams = (names) => {
    const clean = names.map(s => (s || '').trim()).filter(Boolean);
    const teams = clean.map((name, i) => ({ id: i + 1, name, color: TN_COLORS[i % TN_COLORS.length] }));
    const bracketSize = tnNextPow2(teams.length);
    setState({ phase: 'ready', teams, bracketSize, slots: null, results: {} });
  };

  const editTeams = () => setState(s => ({ ...s, phase: 'setup' }));

  const reset = () => {
    if (!window.confirm('トーナメントをリセットして、最初からやり直しますか？')) return;
    clearTimers();
    setState(null);
  };

  const startDraw = () => {
    if (!state || draw.open) return;
    const finalSlots = tnDrawSlots(state.teams, state.bracketSize);
    runDrawAnimation(finalSlots);
  };

  const runDrawAnimation = (finalSlots) => {
    clearTimers();
    setDraw({ open: true, spinning: true, pair: randomPreviewPair(), label: '組み合わせ抽選中…' });
    const names = state.teams.map(t => t.name);
    const total = 22;
    for (let i = 1; i <= total; i++) {
      const t = setTimeout(() => {
        setDraw(d => ({ ...d, pair: randomPreviewPair(), label: '組み合わせ抽選中…' }));
      }, i * 95);
      timersRef.current.push(t);
    }
    // settle: reveal first real matchup, then commit
    const settle = setTimeout(() => {
      const a = teamsById[finalSlots[0]] || { name: '—' };
      const bSlot = finalSlots[1];
      const b = bSlot === 'BYE' ? { name: '不戦勝（シード）' } : (teamsById[bSlot] || { name: '—' });
      setDraw({ open: true, spinning: false, pair: { a: a.name, b: b.name }, label: '組み合わせ決定！' });
    }, total * 95 + 100);
    timersRef.current.push(settle);
    const commit = setTimeout(() => {
      setDraw({ open: false, spinning: false, pair: null, label: '' });
      setState(s => ({ ...s, phase: 'running', slots: finalSlots, results: {} }));
    }, total * 95 + 1300);
    timersRef.current.push(commit);
  };

  const randomPreviewPair = () => {
    const names = state.teams.map(t => t.name);
    if (names.length < 2) return { a: names[0] || '—', b: '—' };
    let i = Math.floor(Math.random() * names.length);
    let j = Math.floor(Math.random() * names.length);
    while (j === i) j = Math.floor(Math.random() * names.length);
    return { a: names[i], b: names[j] };
  };

  const skipDraw = () => {
    clearTimers();
    const finalSlots = tnDrawSlots(state.teams, state.bracketSize);
    setDraw({ open: false, spinning: false, pair: null, label: '' });
    setState(s => ({ ...s, phase: 'running', slots: finalSlots, results: {} }));
  };

  const reshuffle = () => {
    if (state.results && Object.keys(state.results).length > 0) {
      if (!window.confirm('組み合わせを引き直すと、現在の勝敗はリセットされます。よろしいですか？')) return;
    }
    const finalSlots = tnDrawSlots(state.teams, state.bracketSize);
    runDrawAnimation(finalSlots);
  };

  const pickWinner = (matchId, teamId) => {
    setState(s => {
      const cur = (s.results || {})[matchId];
      const next = { ...(s.results || {}) };
      if (cur === teamId) delete next[matchId];
      else next[matchId] = teamId;
      const pruned = tnPrune(s.slots, s.bracketSize, next);
      return { ...s, results: pruned };
    });
    setRecentWin({ matchId, teamId });
    const t = setTimeout(() => setRecentWin(null), 600);
    timersRef.current.push(t);
  };

  /* ---- render ---- */
  if (!state || state.phase === 'setup') {
    return <TnSetup initial={state} onConfirm={confirmTeams} />;
  }

  // sizing: tighten columns as rounds grow so it fits big displays
  const totalRounds = Math.round(Math.log2(state.bracketSize));
  const colWidth = totalRounds >= 5 ? 178 : totalRounds === 4 ? 196 : totalRounds === 3 ? 214 : 240;
  const gap = totalRounds >= 5 ? 24 : totalRounds === 4 ? 30 : 40;

  return (
    <div className="tn-stage">
      <div className="tn-controls">
        <h2 className="tn-controls-title">
          トーナメント表
          <small>{state.teams.length}チーム ／ {state.bracketSize}枠ブラケット ・ シングルイリミネーション</small>
        </h2>
        {state.phase === 'running' && (
          <div className="tn-tabs">
            <button className={view === 'bracket' ? 'is-active' : ''} onClick={() => setView('bracket')}>トーナメント表</button>
            <button className={view === 'ranking' ? 'is-active' : ''} onClick={() => setView('ranking')}>順位表</button>
          </div>
        )}
        <div className="tn-controls-spacer"></div>
        {state.phase === 'running' && (
          <button className="tn-shuffle-btn" onClick={reshuffle} disabled={draw.open}>
            <span className="ico">🎲</span>組み合わせを引き直す
          </button>
        )}
        <button className="tn-edit-btn" onClick={editTeams}>✎ チームを編集</button>
        <button className="tn-bar-reset" onClick={reset}>⟲ リセット</button>
      </div>

      {state.phase === 'ready' && (
        <TnPredraw state={state} onDraw={startDraw} onEdit={editTeams} disabled={draw.open} />
      )}

      {state.phase === 'running' && rounds && view === 'bracket' && (
        <TnBracket
          rounds={rounds}
          teamsById={teamsById}
          onPick={pickWinner}
          recentWin={recentWin}
          champion={champion}
          colWidth={colWidth}
          gap={gap}
        />
      )}

      {state.phase === 'running' && rounds && view === 'ranking' && (
        <TnRanking ranking={ranking} />
      )}

      {draw.open && (
        <TnDrawOverlay draw={draw} onSkip={skipDraw} />
      )}

      {champOpen && champion && (
        <TnWinOverlay champion={champion} teams={state.teams}
          onClose={() => setChampOpen(false)}
          onShowRanking={() => { setView('ranking'); setChampOpen(false); }}
          onReset={reset} />
      )}
    </div>
  );
}

/* ===================================================================
   SETUP — チーム名登録
   =================================================================== */
function TnSetup({ initial, onConfirm }) {
  const init = () => {
    if (initial && initial.teams && initial.teams.length) {
      const names = initial.teams.map(t => t.name);
      while (names.length < TN_MIN) names.push('');
      return names;
    }
    return ['', '', '', ''];
  };
  const [names, setNames] = React.useState(init);

  const filled = names.filter(n => (n || '').trim()).length;
  const canConfirm = filled >= TN_MIN;
  const bracketSize = tnNextPow2(Math.max(filled, TN_MIN));
  const byes = bracketSize - filled;

  const setAt = (i, v) => setNames(arr => arr.map((n, k) => (k === i ? v : n)));
  const addRow = () => setNames(arr => (arr.length < TN_MAX ? [...arr, ''] : arr));
  const delRow = (i) => setNames(arr => (arr.length > TN_MIN ? arr.filter((_, k) => k !== i) : arr));

  return (
    <div className="tn-setup">
      <div className="tn-setup-head">
        <div className="tn-setup-label">◤ TOURNAMENT SETUP</div>
        <h1 className="tn-setup-title">出場チームを<span className="hl">登録</span></h1>
        <p className="tn-setup-sub">
          参加するチーム名を入力してください（{TN_MIN}〜{TN_MAX}チーム）。
          入力が終わったら「組み合わせ抽選へ」を押すと、対戦表の抽選に進みます。
        </p>
      </div>

      <div className="tn-teams-panel">
        <div className="tn-teams-panel-head">
          <h2 className="tn-teams-panel-h">チーム名</h2>
          <div className="tn-teams-count">
            登録 <b>{filled}</b> チーム ／ {bracketSize}枠{byes > 0 ? `（シード ${byes}）` : ''}
          </div>
        </div>

        <div className="tn-team-rows">
          {names.map((name, i) => (
            <div className="tn-team-row" key={i}>
              <span className="tn-team-seed" style={{ background: TN_COLORS[i % TN_COLORS.length] }}>{i + 1}</span>
              <input
                className="tn-team-input"
                value={name}
                maxLength={20}
                placeholder={`チーム${i + 1} の名前`}
                onChange={e => setAt(i, e.target.value)}
              />
              <button className="tn-team-del" onClick={() => delRow(i)}
                disabled={names.length <= TN_MIN} aria-label="削除">✕</button>
            </div>
          ))}
        </div>

        <div className="tn-setup-actions">
          <button className="tn-add-btn" onClick={addRow} disabled={names.length >= TN_MAX}>
            <span className="plus">＋</span> チームを追加
          </button>
          <button className="tn-confirm-btn" onClick={() => onConfirm(names)} disabled={!canConfirm}>
            組み合わせ抽選へ →
          </button>
        </div>
      </div>

      <div className="tn-setup-hint">
        <b>シングルイリミネーション（勝ち抜き戦）。</b>
        負けたら敗退、勝ち続けたチームが優勝です。
        チーム数が {`2のn乗`} でないときは、<b>不戦勝（シード）</b>で自動調整します（例：6チーム → 8枠・シード2）。
      </div>
    </div>
  );
}

/* ===================================================================
   PRE-DRAW — 確定後・抽選前
   =================================================================== */
function TnPredraw({ state, onDraw, onEdit, disabled }) {
  return (
    <div className="tn-predraw">
      <div className="tn-predraw-inner">
        <div className="tn-predraw-label">◤ READY TO DRAW</div>
        <h2 className="tn-predraw-title"><span className="hl">{state.teams.length}チーム</span> でトーナメント開始</h2>
        <p className="tn-predraw-sub">
          下のボタンを押すと、対戦相手・組み合わせを抽選します。
          {state.bracketSize - state.teams.length > 0
            ? `${state.bracketSize}枠ブラケット・シード${state.bracketSize - state.teams.length}。`
            : `${state.bracketSize}枠ブラケット。`}
        </p>
        <div className="tn-predraw-chips">
          {state.teams.map(t => (
            <span className="tn-predraw-chip" key={t.id} style={{ background: t.color }}>
              <span className="seed">{t.id}</span>{t.name}
            </span>
          ))}
        </div>
        <div className="tn-predraw-actions">
          <button className="tn-bigdraw-btn" onClick={onDraw} disabled={disabled}>
            <span className="ico">🎲</span>組み合わせ抽選スタート
          </button>
          <button className="tn-predraw-ghost" onClick={onEdit}>チームを編集</button>
        </div>
      </div>
    </div>
  );
}

/* ===================================================================
   DRAW OVERLAY — ガチャ抽選演出
   =================================================================== */
function TnDrawOverlay({ draw, onSkip }) {
  return (
    <div className={'tn-draw-overlay is-open'}>
      <div className="tn-draw-label">◤ {draw.label}</div>
      <div className={'tn-draw-capsule' + (draw.spinning ? ' is-spinning' : '')}>
        <div className="tn-draw-pair">
          <span className="tn-draw-team">{draw.pair ? draw.pair.a : ''}</span>
          <span className="tn-draw-vs">VS</span>
          <span className="tn-draw-team">{draw.pair ? draw.pair.b : ''}</span>
        </div>
      </div>
      <div className="tn-draw-bracketno">{draw.spinning ? '抽選マシン回転中…' : 'まもなく対戦表が完成します'}</div>
      {draw.spinning && <button className="tn-draw-skip" onClick={onSkip}>スキップして決定 →</button>}
    </div>
  );
}

/* ===================================================================
   CHAMPION OVERLAY — 優勝の派手な演出
   =================================================================== */
function TnConfetti() {
  const pieces = React.useMemo(() => {
    const cols = ['#FFC21A', '#FF7A1A', '#E6421A', '#4FA3D1', '#6FB76A', '#F5EFE0'];
    return Array.from({ length: 90 }, (_, i) => ({
      left: Math.random() * 100,
      bg: cols[i % cols.length],
      delay: Math.random() * 2.2,
      dur: 2.6 + Math.random() * 2.4,
      w: 8 + Math.random() * 8,
      h: 10 + Math.random() * 12,
      round: Math.random() > 0.6,
    }));
  }, []);
  return (
    <div className="tn-confetti">
      {pieces.map((p, i) => (
        <i key={i} style={{
          left: p.left + 'vw', background: p.bg,
          width: p.w + 'px', height: p.h + 'px',
          borderRadius: p.round ? '50%' : '2px',
          animationDelay: p.delay + 's', animationDuration: p.dur + 's',
        }}></i>
      ))}
    </div>
  );
}

function TnWinOverlay({ champion, teams, onClose, onShowRanking, onReset }) {
  return (
    <React.Fragment>
      <TnConfetti />
      <div className="tn-win-overlay is-open">
        <div className="tn-win-rays"></div>
        <div className="tn-win-card">
          <div className="tn-win-label">◤ TOURNAMENT CHAMPION</div>
          <div className="tn-win-crown">👑</div>
          <img className="tn-win-shield" src="assets/shield-emblem.png" alt="シールドエンブレム" />
          <div className="tn-win-champ-of">{teams.length}チームの頂点に立ったのは</div>
          <div className="tn-win-name">{champion.name}</div>
          <p className="tn-win-sub">
            優勝おめでとう！ チーム全員に<b>シールドエンブレムカード</b>を贈呈。<br />
            生態系の頂点を制したのは、あなたたちです。
          </p>
          <div className="tn-win-actions">
            <button className="tn-win-btn" onClick={onShowRanking}>順位表を見る</button>
            <button className="tn-win-btn ghost" onClick={onClose}>トーナメント表を見る</button>
            <button className="tn-win-btn ghost" onClick={onReset}>新しい大会をはじめる</button>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
}

/* ===================================================================
   RANKING — 順位表
   =================================================================== */
function TnRanking({ ranking }) {
  if (!ranking) return null;
  const { entries, complete } = ranking;
  return (
    <div className="tn-ranking">
      <div className="tn-rank-note">
        {complete
          ? '◤ FINAL STANDINGS ／ 勝ち進んだラウンドで順位が決まります（同ラウンド敗退は同順位）'
          : '◤ LIVE STANDINGS ／ 進行中。決勝が決まると最終順位が確定します'}
      </div>
      <div className="tn-rank-list">
        {entries.map((e, i) => {
          let cls = 'tn-rank-row';
          if (e.alive) cls += ' is-alive';
          else if (e.rank === 1) cls += ' is-rank1';
          else if (e.rank === 2) cls += ' is-rank2';
          else if (e.rank === 3) cls += ' is-rank3';
          return (
            <div className={cls} key={e.team.id}>
              <div className="tn-rank-num">{e.alive ? '—' : e.rank}</div>
              <div className="tn-rank-team">
                <span className="tn-rank-swatch" style={{ background: e.team.color }}></span>
                <span className="tn-rank-name">{e.team.name}</span>
              </div>
              <div className="tn-rank-label">
                {e.rank === 1 && <img className="tn-rank-shield" src="assets/shield-emblem.png" alt="" />}
                {e.label}{e.tie ? '（タイ）' : ''}
                {e.alive && <span className="tn-rank-stage">{e.stage}まで進出</span>}
              </div>
              <div className="tn-rank-wins"><b>{e.wins}</b>勝</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('tn-app')).render(<TnApp />);
