// Pickleball Manager - Frontend SPA (React via CDN + Babel Standalone)
const { useState, useEffect, useRef, useCallback, useMemo } = React;

// ===== API Helpers =====
const api = {
  async req(path, opts = {}) {
    const headers = { 'Content-Type': 'application/json', ...(opts.headers || {}) };
    const passcode = localStorage.getItem('pb.passcode');
    if (passcode) headers['x-passcode'] = passcode;
    const res = await fetch(path, { ...opts, headers });
    const data = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
    return data;
  },
  get(p) { return this.req(p); },
  post(p, body) { return this.req(p, { method: 'POST', body: JSON.stringify(body || {}) }); },
  patch(p, body) { return this.req(p, { method: 'PATCH', body: JSON.stringify(body || {}) }); },
};

// ===== AudioContext (アンロック) =====
let audioCtx = null;
function unlockAudio() {
  if (audioCtx) return;
  try {
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    const osc = audioCtx.createOscillator();
    const gain = audioCtx.createGain();
    gain.gain.value = 0;
    osc.connect(gain).connect(audioCtx.destination);
    osc.start();
    osc.stop(audioCtx.currentTime + 0.01);
  } catch(e) {}
}
function playAlarm() {
  if (!audioCtx) return;
  const ctx = audioCtx;
  const t0 = ctx.currentTime;
  for (let i = 0; i < 3; i++) {
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.frequency.value = 880;
    osc.type = 'square';
    gain.gain.setValueAtTime(0, t0 + i * 0.5);
    gain.gain.linearRampToValueAtTime(0.3, t0 + i * 0.5 + 0.02);
    gain.gain.linearRampToValueAtTime(0, t0 + i * 0.5 + 0.35);
    osc.connect(gain).connect(ctx.destination);
    osc.start(t0 + i * 0.5);
    osc.stop(t0 + i * 0.5 + 0.4);
  }
}

// ===== Local Storage =====
const ls = {
  get(k, def = null) {
    try { const v = localStorage.getItem(k); return v == null ? def : JSON.parse(v); } catch { return def; }
  },
  set(k, v) { localStorage.setItem(k, JSON.stringify(v)); },
  del(k) { localStorage.removeItem(k); },
};

// ===== ピックルボール ラケット&ボール フォト =====
// 新画像は縦長 (894x1024) で「PICKLEBALL MANAGER」文字入り。
// 画像自体がロゴを兼ねるため、画面ではタイトル文字を冗長に重ねず簡潔にする。
function PickleballLogo({ className = "w-full max-w-xs" }) {
  return (
    <img
      src="/static/pickleball-hero.jpg"
      alt="Pickleball Manager - racket and balls"
      className={className + " rounded-3xl shadow-2xl shadow-lime-500/10 ring-1 ring-lime-500/20 object-contain"}
      loading="eager"
      decoding="async"
    />
  );
}

// ===== タイトル画面（新仕様：3ボタン + アプリ説明モーダル） =====
function TitlePage({ onChooseHost, onChooseJoin }) {
  const [showAbout, setShowAbout] = useState(false);
  return (
    <div className="min-h-screen bg-gradient-to-b from-navy-950 via-navy-900 to-navy-950 flex flex-col safe-top safe-bottom">
      <div className="flex-1 flex flex-col items-center justify-center px-6 py-6">
        <div className="text-center mb-6">
          <div className="flex justify-center mb-3">
            <PickleballLogo className="w-full max-w-[260px] drop-shadow-[0_8px_24px_rgba(190,242,100,0.25)]" />
          </div>
          <p className="text-slate-400 text-sm mt-2">大人数ピックルボール大会の<br/>進行管理アプリ</p>
        </div>
        <div className="w-full max-w-sm space-y-3">
          <button onClick={onChooseHost}
            className="w-full py-5 bg-lime-500 text-navy-950 font-bold text-lg rounded-2xl active:scale-95 transition shadow-lg shadow-lime-500/20">
            <i className="fa-solid fa-crown mr-2"></i>主催する
          </button>
          <button onClick={onChooseJoin}
            className="w-full py-5 bg-navy-800 text-white font-bold text-lg rounded-2xl border-2 border-lime-500/40 active:scale-95 transition">
            <i className="fa-solid fa-right-to-bracket mr-2"></i>参加する
          </button>
          <button onClick={() => setShowAbout(true)}
            className="w-full py-4 bg-transparent text-slate-300 font-bold rounded-2xl border border-navy-700 active:scale-95 transition">
            <i className="fa-solid fa-circle-info mr-2"></i>アプリ説明
          </button>
        </div>
      </div>
      <footer className="text-center text-xs text-slate-500 py-4">© Pickleball Manager</footer>
      {showAbout && <AboutModal onClose={() => setShowAbout(false)} />}
    </div>
  );
}

function AboutModal({ onClose }) {
  return (
    <div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-end sm:items-center justify-center p-0 sm:p-4" onClick={onClose}>
      <div className="bg-navy-800 rounded-t-3xl sm:rounded-3xl p-6 max-w-lg w-full max-h-[90vh] overflow-y-auto" onClick={e=>e.stopPropagation()}>
        <div className="flex items-center justify-between mb-4">
          <h2 className="text-xl font-bold text-lime-400"><i className="fa-solid fa-circle-info mr-2"></i>アプリ説明</h2>
          <button onClick={onClose} className="w-9 h-9 rounded-full bg-navy-700 text-slate-300"><i className="fa-solid fa-xmark"></i></button>
        </div>
        <div className="space-y-4 text-sm text-slate-200 leading-relaxed">
          <section>
            <h3 className="font-bold text-lime-300 mb-1">🥒 これは何？</h3>
            <p>大人数（4人〜30人以上）でピックルボールを楽しむときの「コート割り」「待機者ローテーション」「タイマー」「スコア」をスマホ1つで管理できるアプリです。</p>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">👑 主催する人がやること</h3>
            <ol className="list-decimal pl-5 space-y-1">
              <li>「主催する」から、コート数・ラウンド数・1試合の時間を設定</li>
              <li>4桁のホストPINを決める（あなただけが知る管理用パスワード）</li>
              <li>10桁のパスコードが発行されるので、それを参加者に共有</li>
              <li>みんなが入室したら「マッチメイキング開始」をタップ</li>
            </ol>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">🎾 参加する人がやること</h3>
            <ol className="list-decimal pl-5 space-y-1">
              <li>「参加する」から10桁パスコードを入力</li>
              <li>自分の名前を入力（一度登録するとこの端末では変更できません）</li>
              <li>あとは画面の指示に従って自分のコートへ！</li>
            </ol>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">🔄 自動マッチメイキングの特徴</h3>
            <ul className="list-disc pl-5 space-y-1">
              <li>「直前のラウンドで休んでいた人」が次のコートに最優先で入る</li>
              <li>遅参・休憩・早退者を無理に追いつかせない（連続出場を防ぐ）</li>
              <li>ダブルスでは、同じパートナー・対戦相手の重複を最小化</li>
              <li>誰かが「休む／帰る／入る」した瞬間、未実施ラウンドだけ自動で再計算</li>
            </ul>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">⏱️ コート別タイマー</h3>
            <p>各コートに独立したタイマーがあり、Start / Stop / Reset 操作は <b>そのコートでプレイ中の人</b> または <b>ホスト</b> だけができます。タイマーが0になるとそのコートの参加者にだけアラーム音が鳴ります。</p>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">📊 スコア入力の権限</h3>
            <p>進行中の試合のスコア（+/−）も、<b>そのコートの参加者</b>と<b>ホスト</b>だけが操作できます。他のコートのスコアは閲覧のみ。</p>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">🏆 ランキング機能（任意）</h3>
            <p>ホストの設定で<b>ランキングをON</b>にすると、勝数・得失点差で順位付けされたリーダーボードが全員に表示されます。ガチ大会向け。OFFのときは表示されません（ゆる勝負向け）。</p>
          </section>
          <section>
            <h3 className="font-bold text-lime-300 mb-1">🛡️ いたずら対策</h3>
            <p>パスコードを通過しないと一切の閲覧・操作ができません。一度名前を登録すると、その端末からは別の人になりすませません。マナーの悪い参加者はホストがKickできます。</p>
          </section>
        </div>
        <button onClick={onClose} className="mt-6 w-full py-3 bg-lime-500 text-navy-950 font-bold rounded-xl">閉じる</button>
      </div>
    </div>
  );
}

// ===== 入室フォーム（参加する） =====
function EnterPage({ onBack, onEntered }) {
  const [code, setCode] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const submit = async () => {
    setErr(''); setBusy(true);
    try {
      const passcode = code.toUpperCase().trim();
      const data = await fetch('/api/rooms/verify-passcode', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ passcode })
      }).then(async r => { const j = await r.json(); if (!r.ok) throw new Error(j.error || 'Invalid'); return j; });
      localStorage.setItem('pb.passcode', passcode);
      ls.set('pb.room', { id: data.room_id, name: data.name, passcode });
      onEntered(data.room_id);
    } catch (e) {
      setErr(e.message || 'パスコードが正しくありません');
    } finally { setBusy(false); }
  };
  return (
    <div className="min-h-screen bg-navy-950 flex flex-col">
      <PageHeader title="参加する" onBack={onBack} />
      <div className="flex-1 px-6 py-6 max-w-md mx-auto w-full">
        <div className="space-y-4">
          <div>
            <label className="block text-sm text-slate-300 mb-2">参加用パスコード（10桁）</label>
            <input
              value={code}
              onChange={e => setCode(e.target.value.toUpperCase())}
              maxLength={10}
              autoCapitalize="characters"
              placeholder="例: A2B3C4D5E6"
              className="w-full px-4 py-3 bg-navy-800 border border-navy-700 rounded-xl text-2xl tracking-widest text-center font-mono focus:border-lime-500 focus:outline-none"
            />
          </div>
          {err && <p className="text-red-400 text-sm"><i className="fa-solid fa-triangle-exclamation mr-1"></i>{err}</p>}
          <button onClick={submit} disabled={busy || code.length < 10}
            className="w-full py-4 bg-lime-500 text-navy-950 font-bold rounded-xl disabled:opacity-40 active:scale-95 transition">
            {busy ? <i className="fa-solid fa-spinner fa-spin"></i> : '入室する'}
          </button>
          <p className="text-xs text-slate-500 text-center mt-4">ホストから10桁のパスコードを受け取ってください</p>
        </div>
      </div>
    </div>
  );
}

// ===== ルーム作成フォーム（主催する） =====
function CreatePage({ onBack, onCreated }) {
  const [name, setName] = useState('Pickleball Event');
  const [pin, setPin] = useState('');
  const [courts, setCourts] = useState(2);
  const [matchType, setMatchType] = useState('D');
  const [rounds, setRounds] = useState(8);
  const [minutes, setMinutes] = useState(12);
  const [ranking, setRanking] = useState(false);
  const [hostPlaying, setHostPlaying] = useState(true);
  const [hostName, setHostName] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const submit = async () => {
    setErr('');
    if (!/^\d{4}$/.test(pin)) { setErr('PINは4桁数字で入力してください'); return; }
    if (hostPlaying && !hostName.trim()) { setErr('参加者として参加する場合、あなたの名前を入力してください'); return; }
    setBusy(true);
    try {
      const data = await fetch('/api/rooms', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name, host_pin: pin, courts_count: Number(courts),
          match_type: matchType, total_rounds: Number(rounds), game_minutes: Number(minutes),
          is_ranking_enabled: ranking,
          is_host_playing: hostPlaying,
          host_name: hostPlaying ? hostName.trim() : undefined,
        })
      }).then(async r => { const j = await r.json(); if (!r.ok) throw new Error(j.error || 'fail'); return j; });
      onCreated(data);
    } catch(e) { setErr(e.message); } finally { setBusy(false); }
  };
  return (
    <div className="min-h-screen bg-navy-950 flex flex-col">
      <PageHeader title="主催する" onBack={onBack} />
      <div className="flex-1 px-6 py-6 max-w-md mx-auto w-full">
        <div className="space-y-4">
          <Field label="イベント名">
            <input value={name} onChange={e=>setName(e.target.value)} className="input" />
          </Field>
          <Field label="ホスト管理PIN（4桁数字）">
            <input value={pin} onChange={e=>setPin(e.target.value.replace(/\D/g,''))} maxLength={4} inputMode="numeric"
              placeholder="0000"
              className="input text-2xl tracking-widest text-center font-mono" />
            <p className="text-xs text-slate-500 mt-1">あなただけが知る管理用パスワードです</p>
          </Field>
          <div className="grid grid-cols-2 gap-3">
            <Field label="コート数">
              <input type="number" min="1" max="20" value={courts} onChange={e=>setCourts(e.target.value)} className="input" />
            </Field>
            <Field label="ラウンド数">
              <input type="number" min="1" max="50" value={rounds} onChange={e=>setRounds(e.target.value)} className="input" />
            </Field>
          </div>
          <div className="grid grid-cols-2 gap-3">
            <Field label="形式">
              <div className="flex gap-2">
                <button onClick={()=>setMatchType('S')} className={`flex-1 py-3 rounded-xl font-bold ${matchType==='S'?'bg-lime-500 text-navy-950':'bg-navy-800 text-slate-300'}`}>S</button>
                <button onClick={()=>setMatchType('D')} className={`flex-1 py-3 rounded-xl font-bold ${matchType==='D'?'bg-lime-500 text-navy-950':'bg-navy-800 text-slate-300'}`}>D</button>
              </div>
            </Field>
            <Field label="1試合の時間（分）">
              <input type="number" min="1" max="60" value={minutes} onChange={e=>setMinutes(e.target.value)} className="input" />
            </Field>
          </div>
          <div className="flex items-center justify-between p-3 bg-navy-800 border border-navy-700 rounded-xl">
            <div>
              <div className="font-bold text-sm"><i className="fa-solid fa-trophy text-yellow-400 mr-2"></i>ランキング機能</div>
              <div className="text-xs text-slate-400">ONにすると勝数・得失点差で順位表示</div>
            </div>
            <Toggle on={ranking} onChange={setRanking} />
          </div>
          <div className="p-3 bg-navy-800 border border-navy-700 rounded-xl space-y-2">
            <div className="flex items-center justify-between">
              <div>
                <div className="font-bold text-sm"><i className="fa-solid fa-user-tag text-lime-400 mr-2"></i>あなたも参加する</div>
                <div className="text-xs text-slate-400">OFFなら運営専任（試合に出場しない）</div>
              </div>
              <Toggle on={hostPlaying} onChange={setHostPlaying} />
            </div>
            {hostPlaying && (
              <input value={hostName} onChange={e=>setHostName(e.target.value)} maxLength={20}
                placeholder="あなたの名前（参加者一覧に表示されます）"
                className="input mt-1" />
            )}
          </div>
          {err && <p className="text-red-400 text-sm"><i className="fa-solid fa-triangle-exclamation mr-1"></i>{err}</p>}
          <button onClick={submit} disabled={busy}
            className="w-full py-4 bg-lime-500 text-navy-950 font-bold rounded-xl disabled:opacity-40 active:scale-95 transition">
            {busy ? <i className="fa-solid fa-spinner fa-spin"></i> : '部屋を作成'}
          </button>
        </div>
      </div>
      <style>{`
        .input { width: 100%; padding: 0.75rem 1rem; background: #111a30; border: 1px solid #1a2546; border-radius: 0.75rem; color: white; outline: none; }
        .input:focus { border-color: #a3e635; }
      `}</style>
    </div>
  );
}

function Toggle({ on, onChange }) {
  return (
    <button onClick={() => onChange(!on)}
      className={`relative w-14 h-8 rounded-full transition ${on ? 'bg-lime-500' : 'bg-navy-700'}`}>
      <span className={`absolute top-1 w-6 h-6 bg-white rounded-full shadow transition ${on ? 'left-7' : 'left-1'}`}></span>
    </button>
  );
}

function PageHeader({ title, onBack }) {
  return (
    <header className="bg-navy-900 border-b border-navy-700 px-4 py-3 flex items-center gap-3 safe-top">
      <button onClick={onBack} className="w-9 h-9 rounded-full bg-navy-800 text-slate-300 flex items-center justify-center">
        <i className="fa-solid fa-chevron-left"></i>
      </button>
      <h1 className="text-lg font-bold flex-1">{title}</h1>
    </header>
  );
}

function Field({ label, children }) {
  return (
    <div>
      <label className="block text-sm text-slate-300 mb-2">{label}</label>
      {children}
    </div>
  );
}

// ===== Created Modal (パスコード/PIN表示) =====
function CreatedInfo({ data, onContinue }) {
  return (
    <div className="fixed inset-0 bg-black/70 backdrop-blur-sm z-50 flex items-center justify-center p-4">
      <div className="bg-navy-800 rounded-2xl p-6 max-w-md w-full border border-lime-500/40">
        <h2 className="text-xl font-bold text-lime-400 mb-4">
          <i className="fa-solid fa-circle-check mr-2"></i>作成完了
        </h2>
        <div className="space-y-4">
          <div>
            <div className="text-xs text-slate-400 mb-1">参加者に伝える 10桁パスコード</div>
            <div className="text-3xl font-mono tracking-widest bg-navy-950 rounded-lg py-3 text-center text-lime-400 select-all">{data.passcode}</div>
          </div>
          <div>
            <div className="text-xs text-slate-400 mb-1">あなただけの 4桁ホストPIN</div>
            <div className="text-3xl font-mono tracking-widest bg-navy-950 rounded-lg py-3 text-center text-white select-all">{data.host_pin}</div>
            <p className="text-xs text-yellow-400 mt-2">
              <i className="fa-solid fa-triangle-exclamation mr-1"></i>
              PINは絶対に他人に教えないでください
            </p>
          </div>
        </div>
        <button onClick={onContinue} className="mt-6 w-full py-3 bg-lime-500 text-navy-950 font-bold rounded-xl">
          会場へ入る
        </button>
      </div>
    </div>
  );
}

// ===== Name Registration (Self-Register, Session Lock) =====
function NameRegister({ roomId, onRegistered, onLeave }) {
  const [name, setName] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const submit = async () => {
    if (!name.trim()) return;
    setBusy(true); setErr('');
    try {
      const data = await api.post(`/api/players/${roomId}`, { name: name.trim() });
      ls.set(`pb.me.${roomId}`, { id: data.id, name: data.name });
      onRegistered(data);
    } catch(e) { setErr(e.message); } finally { setBusy(false); }
  };
  return (
    <div className="min-h-screen flex flex-col bg-navy-950">
      <PageHeader title="プレイヤー登録" onBack={onLeave} />
      <div className="flex-1 flex flex-col justify-center px-6">
        <div className="max-w-md mx-auto w-full">
          <h2 className="text-2xl font-bold text-lime-400 mb-2"><i className="fa-solid fa-user-plus mr-2"></i>あなたの名前</h2>
          <p className="text-slate-400 text-sm mb-6">
            <span className="text-yellow-400 text-xs">⚠ 登録後はこの端末では変更できません（なりすまし防止）</span>
          </p>
          <input value={name} onChange={e=>setName(e.target.value)} maxLength={20}
            placeholder="例: 山田たろう"
            className="w-full px-4 py-4 bg-navy-800 border border-navy-700 rounded-xl text-xl text-center focus:border-lime-500 focus:outline-none" />
          {err && <p className="text-red-400 text-sm mt-2">{err}</p>}
          <button onClick={submit} disabled={busy || !name.trim()}
            className="mt-4 w-full py-4 bg-lime-500 text-navy-950 font-bold rounded-xl disabled:opacity-40 active:scale-95">
            {busy ? <i className="fa-solid fa-spinner fa-spin"></i> : '参加する'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ===== Main App (Room Dashboard) =====
function RoomApp({ roomId, onLeave }) {
  const [snapshot, setSnapshot] = useState(null);
  const [error, setError] = useState('');
  const [hostMode, setHostMode] = useState(ls.get('pb.host', false));
  const [hostPin, setHostPin] = useState(ls.get('pb.hostPin', ''));
  const [showHostLogin, setShowHostLogin] = useState(false);
  const [tab, setTab] = useState('court'); // court / timeline / players / ranking
  const me = ls.get(`pb.me.${roomId}`);

  const fetchSnapshot = useCallback(async () => {
    try {
      const data = await api.get(`/api/rooms/${roomId}/snapshot`);
      setSnapshot(data);
      setError('');
    } catch(e) {
      setError(e.message);
      if (e.message.includes('Unauthorized')) {
        ls.del('pb.passcode'); ls.del('pb.room');
        onLeave();
      }
    }
  }, [roomId, onLeave]);

  useEffect(() => {
    fetchSnapshot();
    const id = setInterval(fetchSnapshot, 3000);
    return () => clearInterval(id);
  }, [fetchSnapshot]);

  if (!snapshot) {
    return <div className="min-h-screen flex items-center justify-center text-slate-400"><i className="fa-solid fa-spinner fa-spin text-3xl"></i></div>;
  }
  // ホストモードかつ「運営専任（参加しない）」の場合、me が無くてもダッシュボードに進める
  const isHostOnly = hostMode && !snapshot.room.is_host_playing;
  if (!me && !isHostOnly) {
    return <NameRegister roomId={roomId} onRegistered={() => fetchSnapshot()} onLeave={onLeave} />;
  }
  const myRecord = me ? snapshot.players.find(p => p.id === me.id) : null;
  if (myRecord && myRecord.is_kicked) {
    return (
      <div className="min-h-screen flex flex-col items-center justify-center px-6 text-center">
        <i className="fa-solid fa-ban text-red-500 text-6xl mb-4"></i>
        <h2 className="text-xl font-bold mb-2">あなたはホストにより退室されました</h2>
        <button onClick={() => { ls.del(`pb.me.${roomId}`); ls.del('pb.passcode'); ls.del('pb.room'); onLeave(); }}
          className="mt-6 px-6 py-3 bg-navy-700 rounded-xl">トップへ戻る</button>
      </div>
    );
  }

  const rankingEnabled = !!snapshot.room.is_ranking_enabled;
  // 運営専任ホスト用のダミー me（権限判定で「参加していない」扱いにするため空ID）
  const safeMe = me || { id: '__host_only__', name: 'ホスト' };

  return (
    <div className="min-h-screen pb-32" onClick={unlockAudio} onTouchStart={unlockAudio}>
      <Header room={snapshot.room} onLeave={onLeave} hostMode={hostMode}
        onHostToggle={() => {
          if (hostMode) { setHostMode(false); ls.set('pb.host', false); ls.del('pb.hostPin'); }
          else setShowHostLogin(true);
        }} />

      {me && <NextMatchBanner snapshot={snapshot} me={me} />}

      {hostMode && snapshot.room.status === 'setup' && (
        <HostStartCard roomId={roomId} pin={hostPin} players={snapshot.players} onStarted={fetchSnapshot} />
      )}

      {snapshot.room.status === 'finished' && (
        <div className="mx-4 mt-4 bg-lime-500/10 border border-lime-500/40 rounded-xl p-4 text-center">
          <i className="fa-solid fa-flag-checkered text-2xl text-lime-400 mr-2"></i>
          <span className="font-bold">イベント終了！お疲れさまでした</span>
        </div>
      )}

      {/* タブ */}
      <div className="sticky top-[57px] z-20 bg-navy-950/95 backdrop-blur border-b border-navy-800 px-2 py-2 flex gap-1 overflow-x-auto no-scrollbar">
        <TabBtn active={tab==='court'} onClick={()=>setTab('court')} icon="fa-table-tennis-paddle-ball" label="コート" />
        <TabBtn active={tab==='timeline'} onClick={()=>setTab('timeline')} icon="fa-list-ol" label="対戦表" />
        <TabBtn active={tab==='players'} onClick={()=>setTab('players')} icon="fa-users" label="参加者" />
        {rankingEnabled && (
          <TabBtn active={tab==='ranking'} onClick={()=>setTab('ranking')} icon="fa-trophy" label="ランキング" />
        )}
      </div>

      {tab === 'court' && (
        <CourtMap snapshot={snapshot} me={safeMe} hostMode={hostMode} hostPin={hostPin} onChanged={fetchSnapshot} />
      )}
      {tab === 'timeline' && <Timeline snapshot={snapshot} />}
      {tab === 'players' && (
        <PlayersList snapshot={snapshot} me={safeMe} hostMode={hostMode} hostPin={hostPin} onChanged={fetchSnapshot} hasMe={!!me} />
      )}
      {tab === 'ranking' && rankingEnabled && (
        <RankingTab roomId={roomId} snapshot={snapshot} me={safeMe} />
      )}

      {/* ホストでない参加者向けの「次のR」ボタン（現ラウンド参加者のみ表示） */}
      {!hostMode && me && (
        <ParticipantAdvanceBar roomId={roomId} snapshot={snapshot} me={me} onChanged={fetchSnapshot} />
      )}

      {hostMode && (
        <HostControls roomId={roomId} pin={hostPin} room={snapshot.room} me={safeMe} snapshot={snapshot} onChanged={fetchSnapshot} />
      )}

      {showHostLogin && (
        <HostLoginModal roomId={roomId} onClose={() => setShowHostLogin(false)}
          onSuccess={(pin) => { setHostMode(true); setHostPin(pin); ls.set('pb.host', true); ls.set('pb.hostPin', pin); setShowHostLogin(false); }} />
      )}
    </div>
  );
}

function TabBtn({ active, onClick, icon, label }) {
  return (
    <button onClick={onClick}
      className={`flex-shrink-0 px-4 py-2 rounded-lg text-sm font-bold ${active ? 'bg-lime-500 text-navy-950' : 'bg-navy-800 text-slate-300'}`}>
      <i className={`fa-solid ${icon} mr-1`}></i>{label}
    </button>
  );
}

function Header({ room, onLeave, hostMode, onHostToggle }) {
  const [showCode, setShowCode] = useState(false);
  const passcode = (typeof window !== 'undefined' && localStorage.getItem('pb.passcode')) || '';
  const copy = () => {
    if (!passcode) return;
    try { navigator.clipboard.writeText(passcode); } catch {}
  };
  return (
    <header className="bg-navy-900 border-b border-navy-700 sticky top-0 z-30 safe-top">
      <div className="px-4 py-3 flex items-center gap-3">
        <div className="flex-1 min-w-0">
          <div className="text-xs text-slate-400">{room.match_type === 'D' ? 'ダブルス' : 'シングルス'} / {room.courts_count}コート / R{room.current_round}/{room.total_rounds}</div>
          <h1 className="text-lg font-bold truncate">{room.name}</h1>
        </div>
        <button onClick={()=>setShowCode(true)}
          className="px-3 py-2 rounded-lg bg-lime-500/15 text-lime-300 border border-lime-500/40 text-xs font-bold"
          title="招待コードを表示">
          <i className="fa-solid fa-qrcode mr-1"></i>招待
        </button>
        <button onClick={onHostToggle}
          className={`px-3 py-2 rounded-lg text-xs font-bold ${hostMode ? 'bg-lime-500 text-navy-950' : 'bg-navy-700 text-slate-300'}`}>
          <i className="fa-solid fa-shield-halved mr-1"></i>{hostMode ? 'HOST' : 'Host'}
        </button>
        <button onClick={onLeave} className="px-3 py-2 rounded-lg bg-navy-700 text-slate-300 text-xs">
          <i className="fa-solid fa-arrow-right-from-bracket"></i>
        </button>
      </div>
      {/* 招待コード常時帯 */}
      <div className="px-4 pb-2 -mt-1 flex items-center gap-2">
        <span className="text-[10px] text-slate-400">招待コード</span>
        <button onClick={copy}
          className="font-mono tracking-widest text-lime-300 text-sm bg-navy-950 border border-navy-700 rounded px-2 py-0.5 active:scale-95"
          title="タップでコピー">
          {passcode || '------'}
          <i className="fa-regular fa-copy ml-1.5 text-slate-500 text-[10px]"></i>
        </button>
      </div>
      {showCode && <InviteCodeModal passcode={passcode} room={room} onClose={()=>setShowCode(false)} />}
    </header>
  );
}

function InviteCodeModal({ passcode, room, onClose }) {
  const copy = () => { try { navigator.clipboard.writeText(passcode); } catch {} };
  return (
    <div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4" onClick={onClose}>
      <div className="bg-navy-800 rounded-2xl p-6 max-w-sm w-full border border-lime-500/40" onClick={e=>e.stopPropagation()}>
        <h2 className="text-lg font-bold text-lime-400 mb-1"><i className="fa-solid fa-share-nodes mr-2"></i>参加者を招待</h2>
        <p className="text-xs text-slate-400 mb-4">この10桁コードを参加者に伝えてください</p>
        <div className="text-3xl font-mono tracking-widest bg-navy-950 rounded-xl py-4 text-center text-lime-400 select-all">{passcode}</div>
        <button onClick={copy} className="mt-3 w-full py-2.5 bg-navy-700 text-white rounded-xl text-sm">
          <i className="fa-regular fa-copy mr-1"></i>コードをコピー
        </button>
        <div className="mt-4 text-xs text-slate-400 text-center">
          {room.name}<br/>{room.match_type==='D'?'ダブルス':'シングルス'} / {room.courts_count}コート / 全{room.total_rounds}ラウンド
        </div>
        <button onClick={onClose} className="mt-4 w-full py-3 bg-lime-500 text-navy-950 font-bold rounded-xl">
          閉じる
        </button>
      </div>
    </div>
  );
}

// ===== Next Match Banner =====
function NextMatchBanner({ snapshot, me }) {
  const myNextCourt = useMemo(() => {
    const upcoming = snapshot.matches.filter(m => m.status === 'upcoming').sort((a,b)=>a.round_number - b.round_number)[0];
    if (!upcoming) return null;
    const games = snapshot.games.filter(g => g.match_id === upcoming.id);
    for (const g of games) {
      const ps = snapshot.game_players.filter(gp => gp.game_id === g.id);
      if (ps.some(p => p.player_id === me.id)) return { court: g.court_number, round: upcoming.round_number };
    }
    return null;
  }, [snapshot, me]);

  if (!myNextCourt) return null;
  return (
    <div className="next-banner mx-4 mt-3 bg-lime-500 text-navy-950 rounded-xl p-4 font-bold flex items-center gap-3">
      <i className="fa-solid fa-bullhorn text-2xl"></i>
      <div>
        <div className="text-xs">次だよ！</div>
        <div className="text-lg leading-tight">あなたの次の試合は <span className="text-2xl">コート{myNextCourt.court}</span></div>
        <div className="text-xs opacity-70">第{myNextCourt.round}ラウンド</div>
      </div>
    </div>
  );
}

// ===== Host: Start Event =====
function HostStartCard({ roomId, pin, players, onStarted }) {
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const active = players.filter(p => p.status === 'active' && !p.is_kicked);
  const start = async () => {
    setBusy(true); setErr('');
    try {
      await api.post(`/api/rooms/${roomId}/start`, { pin });
      onStarted();
    } catch(e) { setErr(e.message); } finally { setBusy(false); }
  };
  return (
    <div className="mx-4 mt-3 bg-navy-800 rounded-xl p-4 border border-lime-500/40">
      <h3 className="font-bold text-lime-400 mb-2"><i className="fa-solid fa-play mr-2"></i>イベントを開始</h3>
      <p className="text-sm text-slate-300 mb-3">アクティブな参加者: <span className="font-bold text-white">{active.length}人</span></p>
      {err && <p className="text-red-400 text-sm mb-2">{err}</p>}
      <button onClick={start} disabled={busy || active.length < 2}
        className="w-full py-3 bg-lime-500 text-navy-950 font-bold rounded-xl disabled:opacity-40">
        {busy ? <i className="fa-solid fa-spinner fa-spin"></i> : '🎾 マッチメイキング開始'}
      </button>
    </div>
  );
}

// ===== Court Map (タブ：コート) =====
function CourtMap({ snapshot, me, hostMode, hostPin, onChanged }) {
  const liveMatch = snapshot.matches.find(m => m.status === 'live');
  const upcomingMatch = snapshot.matches.filter(m => m.status === 'upcoming').sort((a,b)=>a.round_number - b.round_number)[0];

  if (!liveMatch && !upcomingMatch) {
    return (
      <div className="mx-4 mt-3 bg-navy-800 rounded-xl p-6 text-center text-slate-400">
        <i className="fa-solid fa-table-tennis-paddle-ball text-4xl mb-2 opacity-50"></i>
        <p>まだ試合が組まれていません</p>
      </div>
    );
  }

  return (
    <div className="mx-4 mt-3 space-y-3">
      {liveMatch && (
        <div>
          <div className="text-xs text-slate-400 mb-1 px-1">▶ Live - 第{liveMatch.round_number}R</div>
          <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
            {snapshot.games.filter(g => g.match_id === liveMatch.id).map(g => (
              <CourtCard key={g.id} game={g} match={liveMatch} snapshot={snapshot} me={me}
                hostMode={hostMode} hostPin={hostPin} onChanged={onChanged} editable />
            ))}
          </div>
        </div>
      )}
      {upcomingMatch && (
        <div>
          <div className="text-xs text-slate-400 mb-1 px-1 mt-2">⏭ Next - 第{upcomingMatch.round_number}R</div>
          <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
            {snapshot.games.filter(g => g.match_id === upcomingMatch.id).map(g => (
              <CourtCard key={g.id} game={g} match={upcomingMatch} snapshot={snapshot} me={me}
                hostMode={hostMode} hostPin={hostPin} onChanged={onChanged} />
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function CourtCard({ game, match, snapshot, me, hostMode, hostPin, onChanged, editable }) {
  const playerName = (id) => snapshot.players.find(p=>p.id===id)?.name ?? '?';
  const team1Ids = snapshot.game_players.filter(gp => gp.game_id === game.id && gp.team === 1).map(gp => gp.player_id);
  const team2Ids = snapshot.game_players.filter(gp => gp.game_id === game.id && gp.team === 2).map(gp => gp.player_id);
  const meHere = team1Ids.includes(me.id) || team2Ids.includes(me.id);
  // 操作権限: ホスト or そのコートの参加者
  const canControl = (hostMode || meHere) && match.status === 'live';

  const [t1, setT1] = useState(game.team1_score);
  const [t2, setT2] = useState(game.team2_score);
  useEffect(() => { setT1(game.team1_score); setT2(game.team2_score); }, [game.team1_score, game.team2_score]);

  const update = async (newT1, newT2) => {
    if (!canControl) return;
    setT1(newT1); setT2(newT2);
    try {
      await api.patch(`/api/games/${snapshot.room.id}/${game.id}/score`, {
        team1_score: newT1, team2_score: newT2,
        pin: hostMode ? hostPin : undefined,
        player_id: me.id,
      });
    } catch(e) { /* silent */ }
  };

  const TeamRow = ({ ids, score, onScore, color }) => (
    <div className={`flex items-center gap-2 p-2 rounded-lg ${color}`}>
      <div className="flex-1 min-w-0">
        {ids.map(id => (
          <div key={id} className={`truncate text-sm ${id===me.id ? 'font-bold text-lime-300' : ''}`}>
            {id===me.id && <i className="fa-solid fa-star text-lime-400 mr-1"></i>}
            {playerName(id)}
          </div>
        ))}
      </div>
      <div className="flex items-center gap-1">
        {canControl && (
          <button onClick={()=>onScore(Math.max(0, score-1))} className="score-btn w-9 h-9 rounded-lg bg-navy-700 text-lg active:bg-navy-600">−</button>
        )}
        <div className={`w-12 text-center text-3xl font-bold font-mono ${canControl ? 'text-white' : 'text-slate-300'}`}>{score}</div>
        {canControl && (
          <button onClick={()=>onScore(score+1)} className="score-btn w-9 h-9 rounded-lg bg-lime-500 text-navy-950 text-lg font-bold active:bg-lime-600">+</button>
        )}
      </div>
    </div>
  );

  return (
    <div className={`bg-navy-800 rounded-xl p-3 border ${meHere ? 'border-lime-500' : 'border-navy-700'}`}>
      <div className="flex items-center justify-between mb-2">
        <div className="font-bold text-lg">
          <i className="fa-solid fa-table-tennis-paddle-ball text-lime-400 mr-2"></i>
          コート {game.court_number}
          {meHere && <span className="ml-2 text-xs px-2 py-0.5 bg-lime-500/20 text-lime-300 rounded-full">YOU</span>}
        </div>
        {match.status === 'live' && <span className="text-xs px-2 py-0.5 bg-red-500 text-white rounded-full font-bold animate-pulse">LIVE</span>}
        {match.status === 'upcoming' && <span className="text-xs px-2 py-0.5 bg-navy-700 text-slate-300 rounded-full">待機</span>}
      </div>

      {/* コート別タイマー (live のみ表示) */}
      {match.status === 'live' && (
        <CourtTimer game={game} canControl={canControl} hostMode={hostMode} hostPin={hostPin}
          me={me} roomId={snapshot.room.id} meHere={meHere} onChanged={onChanged} />
      )}

      <TeamRow ids={team1Ids} score={t1} onScore={(v)=>update(v, t2)} color="bg-navy-900" />
      <div className="text-center text-xs text-slate-500 my-1">VS</div>
      <TeamRow ids={team2Ids} score={t2} onScore={(v)=>update(t1, v)} color="bg-navy-900" />

      {!canControl && match.status === 'live' && (
        <div className="mt-2 text-center text-xs text-slate-500">
          <i className="fa-solid fa-lock mr-1"></i>このコートの操作権限はありません（閲覧のみ）
        </div>
      )}
    </div>
  );
}

// ===== コート別タイマー =====
function CourtTimer({ game, canControl, hostMode, hostPin, me, roomId, meHere, onChanged }) {
  const [tick, setTick] = useState(0);
  const lastAlarmRef = useRef(0);
  useEffect(() => {
    const id = setInterval(() => setTick(t => t+1), 250);
    return () => clearInterval(id);
  }, []);

  // 残時間計算
  let remainMs = game.duration_ms;
  if (game.timer_status === 'running' && game.timer_ends_at) {
    remainMs = Math.max(0, game.timer_ends_at - Date.now());
  } else if (game.timer_status === 'paused' && game.remaining_ms != null) {
    remainMs = game.remaining_ms;
  } else if (game.timer_status === 'idle') {
    remainMs = game.duration_ms;
  }

  // アラーム: タイマーが running で 0 になった瞬間。「該当コートの参加者」または「ホスト」のみ鳴らす
  useEffect(() => {
    if (game.timer_status !== 'running' || !game.timer_ends_at) return;
    const ends = game.timer_ends_at;
    if (Date.now() >= ends && lastAlarmRef.current !== ends) {
      if (meHere || hostMode) {
        lastAlarmRef.current = ends;
        playAlarm();
      }
    }
  }, [tick, game.timer_status, game.timer_ends_at, meHere, hostMode]);

  const m = Math.floor(remainMs / 60000);
  const s = Math.floor((remainMs % 60000) / 1000);
  const mm = String(m).padStart(2, '0');
  const ss = String(s).padStart(2, '0');

  let cls = 'text-lime-400';
  if (game.timer_status === 'running') {
    if (remainMs <= 0) cls = 'timer-danger';
    else if (remainMs < 30000) cls = 'timer-danger';
    else if (remainMs < 60000) cls = 'timer-warning';
  } else if (game.timer_status === 'paused') {
    cls = 'text-yellow-400';
  } else {
    cls = 'text-slate-400';
  }

  const callTimer = async (action) => {
    try {
      await api.post(`/api/games/${roomId}/${game.id}/timer/${action}`, {
        pin: hostMode ? hostPin : undefined,
        player_id: me.id,
      });
      onChanged();
    } catch(e) { alert(e.message); }
  };

  return (
    <div className={`mb-2 rounded-lg p-2 ${canControl ? 'bg-navy-950 border border-navy-700' : 'bg-navy-900/50'}`}>
      <div className="flex items-center justify-between gap-2">
        <div className={`text-3xl font-mono font-bold ${cls}`}>{mm}:{ss}</div>
        <div className="text-xs">
          {game.timer_status === 'running' && <span className="px-2 py-0.5 bg-red-500/20 text-red-300 rounded">RUNNING</span>}
          {game.timer_status === 'paused' && <span className="px-2 py-0.5 bg-yellow-500/20 text-yellow-300 rounded">PAUSED</span>}
          {game.timer_status === 'idle' && <span className="px-2 py-0.5 bg-navy-700 text-slate-300 rounded">IDLE</span>}
        </div>
      </div>
      {canControl ? (
        <div className="flex gap-1 mt-2">
          {game.timer_status !== 'running' ? (
            <button onClick={()=>callTimer('start')} className="flex-1 py-2 bg-lime-500 text-navy-950 rounded-lg text-xs font-bold active:scale-95">
              <i className="fa-solid fa-play mr-1"></i>{game.timer_status === 'paused' ? '再開' : 'Start'}
            </button>
          ) : (
            <button onClick={()=>callTimer('pause')} className="flex-1 py-2 bg-yellow-500 text-navy-950 rounded-lg text-xs font-bold active:scale-95">
              <i className="fa-solid fa-pause mr-1"></i>Stop
            </button>
          )}
          <button onClick={()=>callTimer('reset')} className="flex-1 py-2 bg-navy-700 text-slate-200 rounded-lg text-xs font-bold active:scale-95">
            <i className="fa-solid fa-rotate-right mr-1"></i>Reset
          </button>
        </div>
      ) : (
        <div className="mt-1 text-[10px] text-slate-500 text-center">
          <i className="fa-solid fa-eye mr-1"></i>閲覧のみ
        </div>
      )}
    </div>
  );
}

// ===== Timeline =====
function Timeline({ snapshot }) {
  const matches = snapshot.matches;
  if (matches.length === 0) {
    return (
      <div className="mx-4 mt-3 bg-navy-800 rounded-xl p-6 text-center text-slate-400">
        <i className="fa-solid fa-list-ol text-4xl mb-2 opacity-50"></i>
        <p>対戦表はまだ作成されていません</p>
      </div>
    );
  }
  return (
    <div className="mx-4 mt-3">
      <div className="space-y-2">
        {matches.map(m => {
          const games = snapshot.games.filter(g => g.match_id === m.id);
          let badge, ring;
          if (m.status === 'live') { badge = <span className="text-xs px-2 py-0.5 bg-red-500 text-white rounded-full font-bold animate-pulse">LIVE</span>; ring = 'border-red-500/40'; }
          else if (m.status === 'finished') { badge = <span className="text-xs px-2 py-0.5 bg-slate-600 text-slate-200 rounded-full">FINISHED</span>; ring = 'border-navy-700 opacity-60'; }
          else { badge = <span className="text-xs px-2 py-0.5 bg-lime-500 text-navy-950 rounded-full">UPCOMING</span>; ring = 'border-lime-500/30'; }
          return (
            <div key={m.id} className={`bg-navy-800 rounded-xl p-3 border ${ring}`}>
              <div className="flex items-center justify-between mb-2">
                <div className="font-bold">第 {m.round_number} ラウンド</div>
                {badge}
              </div>
              <div className="space-y-1">
                {games.map(g => {
                  const t1 = snapshot.game_players.filter(gp => gp.game_id === g.id && gp.team === 1).map(gp => snapshot.players.find(p=>p.id===gp.player_id)?.name).filter(Boolean);
                  const t2 = snapshot.game_players.filter(gp => gp.game_id === g.id && gp.team === 2).map(gp => snapshot.players.find(p=>p.id===gp.player_id)?.name).filter(Boolean);
                  return (
                    <div key={g.id} className="text-sm bg-navy-900 rounded px-2 py-1 flex items-center gap-2">
                      <span className="text-xs text-slate-400 shrink-0">C{g.court_number}</span>
                      <span className="flex-1 truncate">{t1.join(' / ')}</span>
                      <span className="text-slate-500">{m.status==='finished' || (g.team1_score>0||g.team2_score>0) ? `${g.team1_score}-${g.team2_score}` : 'vs'}</span>
                      <span className="flex-1 truncate text-right">{t2.join(' / ')}</span>
                    </div>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ===== Players List =====
function PlayersList({ snapshot, me, hostMode, hostPin, onChanged, hasMe = true }) {
  const players = snapshot.players.filter(p => !p.is_kicked || p.id === me.id);
  const myStatus = players.find(p => p.id === me.id)?.status ?? 'active';

  const setMyStatus = async (s) => {
    if (!hasMe) return;
    try {
      await api.patch(`/api/players/${snapshot.room.id}/${me.id}/status`, { status: s });
      onChanged();
    } catch(e) { alert(e.message); }
  };

  const kick = async (pid, name) => {
    if (!confirm(`${name} さんを退室させますか？`)) return;
    try {
      await api.post(`/api/players/${snapshot.room.id}/${pid}/kick`, { pin: hostPin });
      onChanged();
    } catch(e) { alert(e.message); }
  };

  const statusBtn = (val, label, color) => (
    <button onClick={()=>setMyStatus(val)} className={`flex-1 py-2 rounded-lg text-sm font-bold ${myStatus===val ? color : 'bg-navy-800 text-slate-400'}`}>{label}</button>
  );

  return (
    <div className="mx-4 mt-3">
      {hasMe && (
        <div className="bg-navy-800 rounded-xl p-3 mb-3 border border-navy-700">
          <div className="text-xs text-slate-400 mb-2">あなた（{me.name}）のステータス</div>
          <div className="flex gap-2">
            {statusBtn('active', '参加中', 'bg-lime-500 text-navy-950')}
            {statusBtn('resting', '休憩', 'bg-yellow-500 text-navy-950')}
            {statusBtn('left', '帰る', 'bg-slate-500 text-white')}
          </div>
        </div>
      )}
      {!hasMe && (
        <div className="bg-navy-800 rounded-xl p-3 mb-3 border border-navy-700 text-xs text-slate-400">
          <i className="fa-solid fa-shield-halved text-lime-400 mr-1"></i>
          あなたは <span className="font-bold text-lime-300">運営専任ホスト</span> として参加しています（試合には出場しません）
        </div>
      )}
      <div className="bg-navy-800 rounded-xl divide-y divide-navy-700 border border-navy-700">
        {players.map(p => {
          const isMe = p.id === me.id;
          const dot = p.status === 'active' ? 'bg-lime-400' : p.status === 'resting' ? 'bg-yellow-400' : 'bg-slate-500';
          const label = p.status === 'active' ? '参加中' : p.status === 'resting' ? '休憩' : '帰宅';
          return (
            <div key={p.id} className="flex items-center gap-3 px-3 py-2.5">
              <span className={`w-2.5 h-2.5 rounded-full ${dot}`}></span>
              <div className="flex-1 min-w-0">
                <div className={`truncate ${isMe ? 'font-bold text-lime-300' : ''}`}>
                  {isMe && <i className="fa-solid fa-star text-lime-400 mr-1"></i>}
                  {p.name}
                </div>
                <div className="text-xs text-slate-500">{label} / {p.games_played}試合</div>
              </div>
              {hostMode && !isMe && !p.is_kicked && (
                <button onClick={()=>kick(p.id, p.name)} className="text-xs px-2 py-1 bg-red-500/20 text-red-400 rounded">
                  <i className="fa-solid fa-user-slash mr-1"></i>Kick
                </button>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ===== Ranking Tab =====
function RankingTab({ roomId, snapshot, me }) {
  const [data, setData] = useState({ enabled: true, entries: [] });
  const [loading, setLoading] = useState(true);
  const fetchRanking = useCallback(async () => {
    try {
      const r = await api.get(`/api/rooms/${roomId}/ranking`);
      setData(r);
    } catch(e) {} finally { setLoading(false); }
  }, [roomId]);
  useEffect(() => {
    fetchRanking();
    const id = setInterval(fetchRanking, 5000);
    return () => clearInterval(id);
  }, [fetchRanking]);

  if (loading) return <div className="text-center text-slate-400 mt-8"><i className="fa-solid fa-spinner fa-spin"></i></div>;

  if (!data.entries || data.entries.length === 0) {
    return (
      <div className="mx-4 mt-3 bg-navy-800 rounded-xl p-6 text-center text-slate-400">
        <i className="fa-solid fa-trophy text-4xl mb-2 opacity-50"></i>
        <p>まだ集計対象の試合がありません</p>
        <p className="text-xs mt-1">試合が完了すると順位が表示されます</p>
      </div>
    );
  }

  const medal = (i) => i===0?'🥇':i===1?'🥈':i===2?'🥉':`${i+1}`;

  return (
    <div className="mx-4 mt-3">
      <div className="bg-navy-800 rounded-xl border border-navy-700 overflow-hidden">
        <div className="grid grid-cols-12 px-3 py-2 text-xs text-slate-400 border-b border-navy-700">
          <div className="col-span-1 text-center">順位</div>
          <div className="col-span-5">名前</div>
          <div className="col-span-2 text-center">勝</div>
          <div className="col-span-2 text-center">試</div>
          <div className="col-span-2 text-center">得失</div>
        </div>
        {data.entries.map((e, i) => {
          const isMe = e.player_id === me.id;
          return (
            <div key={e.player_id}
              className={`grid grid-cols-12 px-3 py-2.5 border-b border-navy-700 last:border-b-0 items-center ${isMe ? 'bg-lime-500/10' : ''}`}>
              <div className="col-span-1 text-center text-lg">{medal(i)}</div>
              <div className={`col-span-5 truncate ${isMe ? 'font-bold text-lime-300' : ''}`}>
                {isMe && <i className="fa-solid fa-star text-lime-400 mr-1"></i>}
                {e.name}
              </div>
              <div className="col-span-2 text-center font-bold text-lime-400">{e.wins}</div>
              <div className="col-span-2 text-center text-slate-300">{e.gp}</div>
              <div className={`col-span-2 text-center font-mono ${e.diff>0?'text-lime-400':e.diff<0?'text-red-400':'text-slate-300'}`}>
                {e.diff > 0 ? `+${e.diff}` : e.diff}
              </div>
            </div>
          );
        })}
      </div>
      <p className="text-xs text-slate-500 mt-2 text-center">完了済みの試合のみ集計（並び順: 勝数 → 得失点差 → 試合数）</p>
    </div>
  );
}

// ===== Host Login Modal =====
function HostLoginModal({ roomId, onClose, onSuccess }) {
  const [pin, setPin] = useState('');
  const [err, setErr] = useState('');
  const [busy, setBusy] = useState(false);
  const submit = async () => {
    setBusy(true); setErr('');
    try {
      await api.post(`/api/rooms/${roomId}/verify-pin`, { pin });
      onSuccess(pin);
    } catch(e) { setErr('PINが違います'); } finally { setBusy(false); }
  };
  return (
    <div className="fixed inset-0 bg-black/70 z-50 flex items-center justify-center p-4" onClick={onClose}>
      <div className="bg-navy-800 rounded-2xl p-6 max-w-sm w-full" onClick={e=>e.stopPropagation()}>
        <h2 className="text-lg font-bold mb-3"><i className="fa-solid fa-shield-halved text-lime-400 mr-2"></i>ホスト認証</h2>
        <input value={pin} onChange={e=>setPin(e.target.value.replace(/\D/g,''))} maxLength={4} inputMode="numeric"
          placeholder="0000"
          className="w-full px-4 py-3 bg-navy-950 rounded-xl text-2xl tracking-widest text-center font-mono" />
        {err && <p className="text-red-400 text-sm mt-2">{err}</p>}
        <button onClick={submit} disabled={busy || pin.length !== 4} className="mt-4 w-full py-3 bg-lime-500 text-navy-950 font-bold rounded-xl disabled:opacity-40">
          認証
        </button>
      </div>
    </div>
  );
}

// ===== 現ラウンド参加者向け「次のR」バー =====
function ParticipantAdvanceBar({ roomId, snapshot, me, onChanged }) {
  const liveMatch = snapshot.matches.find(m => m.status === 'live');
  const inLive = useMemo(() => {
    if (!liveMatch) return false;
    const games = snapshot.games.filter(g => g.match_id === liveMatch.id);
    for (const g of games) {
      const ps = snapshot.game_players.filter(gp => gp.game_id === g.id);
      if (ps.some(p => p.player_id === me.id)) return true;
    }
    return false;
  }, [snapshot, me, liveMatch]);

  if (!inLive) return null;

  const advance = async () => {
    if (!confirm('現在のラウンドを終了し、次のラウンドへ進みますか？\n（参加者全員に影響します）')) return;
    try {
      await api.post(`/api/rooms/${roomId}/advance`, { player_id: me.id });
      onChanged();
    } catch(e) { alert(e.message); }
  };

  return (
    <div className="fixed bottom-0 left-0 right-0 bg-navy-900 border-t border-lime-500/30 px-3 py-2 safe-bottom z-20">
      <button onClick={advance} className="w-full py-3 bg-lime-500 text-navy-950 rounded-xl text-sm font-bold active:scale-95">
        <i className="fa-solid fa-forward mr-2"></i>このラウンドを終了して次へ
      </button>
      <p className="text-[10px] text-slate-500 text-center mt-1">現在のコート参加者として、ラウンドを進められます</p>
    </div>
  );
}

// ===== Host Controls (bottom) =====
function HostControls({ roomId, pin, room, me, snapshot, onChanged }) {
  const [showSettings, setShowSettings] = useState(false);
  const reset = async (mode) => {
    const msg = mode === 'wipe' ? '本当に全試合データをリセットしますか？' : '残り試合をすべて終了扱いにしますか？';
    if (!confirm(msg)) return;
    try { await api.post(`/api/rooms/${roomId}/reset`, { pin, mode }); onChanged(); } catch(e) { alert(e.message); }
  };
  const advance = async () => {
    if (!confirm('現在のラウンドを終了し、次のラウンドへ進みますか？')) return;
    try { await api.post(`/api/rooms/${roomId}/advance`, { pin }); onChanged(); } catch(e) { alert(e.message); }
  };
  return (
    <>
      <div className="fixed bottom-0 left-0 right-0 bg-navy-900 border-t border-lime-500/30 px-2 py-2 grid grid-cols-4 gap-1 safe-bottom z-20">
        <button onClick={()=>setShowSettings(true)} className="py-2 bg-navy-800 rounded-lg text-xs">
          <i className="fa-solid fa-gear block mb-0.5"></i>設定
        </button>
        <button onClick={advance} className="py-2 bg-lime-500 text-navy-950 rounded-lg text-xs font-bold">
          <i className="fa-solid fa-forward block mb-0.5"></i>次のR
        </button>
        <button onClick={()=>reset('finish')} className="py-2 bg-yellow-500/20 text-yellow-400 rounded-lg text-xs">
          <i className="fa-solid fa-flag-checkered block mb-0.5"></i>強制終了
        </button>
        <button onClick={()=>reset('wipe')} className="py-2 bg-red-500/20 text-red-400 rounded-lg text-xs">
          <i className="fa-solid fa-trash block mb-0.5"></i>リセット
        </button>
      </div>
      {showSettings && (
        <SettingsModal room={room} pin={pin} onClose={()=>setShowSettings(false)} onSaved={()=>{ setShowSettings(false); onChanged(); }} />
      )}
    </>
  );
}

function SettingsModal({ room, pin, onClose, onSaved }) {
  const [courts, setCourts] = useState(room.courts_count);
  const [rounds, setRounds] = useState(room.total_rounds);
  const [minutes, setMinutes] = useState(room.game_minutes);
  const [ranking, setRanking] = useState(!!room.is_ranking_enabled);
  const [hostPlaying, setHostPlaying] = useState(!!room.is_host_playing);
  const [hostName, setHostName] = useState('');
  const [busy, setBusy] = useState(false);
  const save = async () => {
    setBusy(true);
    try {
      await api.patch(`/api/rooms/${room.id}/settings`, {
        pin, courts_count: Number(courts), total_rounds: Number(rounds),
        game_minutes: Number(minutes), is_ranking_enabled: ranking,
        is_host_playing: hostPlaying,
        host_name: hostName.trim() || undefined,
      });
      onSaved();
    } catch(e) { alert(e.message); } finally { setBusy(false); }
  };
  return (
    <div className="fixed inset-0 bg-black/70 z-50 flex items-end sm:items-center justify-center p-4" onClick={onClose}>
      <div className="bg-navy-800 rounded-2xl p-6 max-w-md w-full" onClick={e=>e.stopPropagation()}>
        <h2 className="text-lg font-bold mb-4"><i className="fa-solid fa-gear text-lime-400 mr-2"></i>基本設定</h2>
        <div className="space-y-3">
          <div>
            <label className="block text-sm text-slate-300 mb-1">コート数</label>
            <input type="number" min="1" max="20" value={courts} onChange={e=>setCourts(e.target.value)} className="w-full px-3 py-2 bg-navy-950 rounded-lg" />
          </div>
          <div>
            <label className="block text-sm text-slate-300 mb-1">合計ラウンド数</label>
            <input type="number" min="1" max="50" value={rounds} onChange={e=>setRounds(e.target.value)} className="w-full px-3 py-2 bg-navy-950 rounded-lg" />
          </div>
          <div>
            <label className="block text-sm text-slate-300 mb-1">1試合の時間（分）</label>
            <input type="number" min="1" max="60" value={minutes} onChange={e=>setMinutes(e.target.value)} className="w-full px-3 py-2 bg-navy-950 rounded-lg" />
            <p className="text-xs text-slate-500 mt-1">※コート別タイマーの初期値として使われます</p>
          </div>
          <div className="flex items-center justify-between p-3 bg-navy-950 rounded-xl">
            <div>
              <div className="font-bold text-sm"><i className="fa-solid fa-trophy text-yellow-400 mr-2"></i>ランキング機能</div>
              <div className="text-xs text-slate-400">勝数・得失点差で順位表示</div>
            </div>
            <Toggle on={ranking} onChange={setRanking} />
          </div>
          <div className="p-3 bg-navy-950 rounded-xl space-y-2">
            <div className="flex items-center justify-between">
              <div>
                <div className="font-bold text-sm"><i className="fa-solid fa-user-tag text-lime-400 mr-2"></i>あなたも参加する</div>
                <div className="text-xs text-slate-400">OFFなら運営専任（試合に出場しない）</div>
              </div>
              <Toggle on={hostPlaying} onChange={setHostPlaying} />
            </div>
            {hostPlaying && !room.host_player_id && (
              <input value={hostName} onChange={e=>setHostName(e.target.value)} maxLength={20}
                placeholder="あなたの名前"
                className="w-full px-3 py-2 bg-navy-900 border border-navy-700 rounded-lg" />
            )}
          </div>
        </div>
        <p className="text-xs text-yellow-400 mt-3">※ コート数/ラウンド数/参加状態の変更後、未実施ラウンドは自動的に再計算されます</p>
        <div className="flex gap-2 mt-4">
          <button onClick={onClose} className="flex-1 py-3 bg-navy-700 rounded-xl">キャンセル</button>
          <button onClick={save} disabled={busy} className="flex-1 py-3 bg-lime-500 text-navy-950 font-bold rounded-xl disabled:opacity-40">
            {busy ? <i className="fa-solid fa-spinner fa-spin"></i> : '保存'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ===== Root =====
function App() {
  const [view, setView] = useState('title'); // title / create / enter / room
  const [roomId, setRoomId] = useState(null);
  const [createdInfo, setCreatedInfo] = useState(null);

  useEffect(() => {
    const stored = ls.get('pb.room');
    if (stored?.id) {
      localStorage.setItem('pb.passcode', stored.passcode);
      setRoomId(stored.id);
      setView('room');
    }
  }, []);

  const goTitle = () => {
    ls.del('pb.room'); ls.del('pb.host'); ls.del('pb.hostPin');
    localStorage.removeItem('pb.passcode');
    setRoomId(null);
    setView('title');
  };

  if (createdInfo) {
    return <CreatedInfo data={createdInfo} onContinue={() => {
      localStorage.setItem('pb.passcode', createdInfo.passcode);
      ls.set('pb.room', { id: createdInfo.id, passcode: createdInfo.passcode });
      ls.set('pb.host', true);
      ls.set('pb.hostPin', createdInfo.host_pin);
      // ホストが「参加者として参加」の場合、自動で me を登録（NameRegister 画面をスキップ）
      if (createdInfo.host_player_id) {
        ls.set(`pb.me.${createdInfo.id}`, { id: createdInfo.host_player_id, name: 'ホスト' });
      }
      setRoomId(createdInfo.id);
      setCreatedInfo(null);
      setView('room');
    }} />;
  }

  if (view === 'room' && roomId) {
    return <RoomApp roomId={roomId} onLeave={goTitle} />;
  }
  if (view === 'create') {
    return <CreatePage onBack={() => setView('title')} onCreated={setCreatedInfo} />;
  }
  if (view === 'enter') {
    return <EnterPage onBack={() => setView('title')} onEntered={(id) => { setRoomId(id); setView('room'); }} />;
  }
  return <TitlePage
    onChooseHost={() => setView('create')}
    onChooseJoin={() => setView('enter')}
  />;
}

ReactDOM.createRoot(document.getElementById('app')).render(<App />);
