/* results.jsx — medal table + all final results (phase-aware emptiness) */
// チーム別 進出状況：karal速報の qualify（→準決/→A決勝 等）から、各クルーの「最新の行き先」を集計。
// 1日目はメダルが確定しないため、これが実質の「今日の強さ」ランキングになる。

// 各バケット（行き先）の定義。round は発艇時刻を引くためのラウンド名（敗退は行き先なし）。
const PROG_BUCKETS = [
  { key: "afinal", label: "A決勝", cls: "prog-afinal", round: "A決勝" },
  { key: "semi", label: "準決", cls: "prog-semi", round: "準決" },
  { key: "bfinal", label: "B決勝", cls: "prog-bfinal", round: "B決勝" },
  { key: "cr", label: "敗者戦", cls: "prog-cr", round: "敗者戦" },
  { key: "out", label: "敗退", cls: "pgt-out", round: null },
];

// (evKey, teamLabel) → そのクルーの選手名 [[seat,name,kana],...]
// entries の並び順(n=1始まり)が ROSTERS のキー。高校クルーは ROSTERS が無く空配列＝氏名は出さない（保護）。
function crewAthletes(evKey, teamLabel) {
  const RW = window.RW;
  const ev = (RW.EVENTS || []).find((e) => e.key === evKey);
  if (!ev || !ev.entries) return [];
  const base = RW.baseTeam(teamLabel);
  const entry = ev.entries.find((en) => en.team === teamLabel) || ev.entries.find((en) => RW.baseTeam(en.team) === base);
  if (!entry) return [];
  const ros = (window.KANSAI.ROSTERS || {})[evKey] || {};
  return ros[entry.n] || [];
}

function buildProgressTable() {
  const RW = window.RW;
  const rl = (r) => r === "敗復" ? "敗者戦" : r === "決勝" ? "A決勝" : r;
  const norm = (s) => String(s || "").replace(/[\s　]/g, "");
  // karal.jpは「→Ａ決勝」など全角英字で配信するため半角へ正規化してから判定する。
  const zen = (s) => String(s || "").replace(/[Ａ-Ｚａ-ｚ０-９]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xFEE0));
  const isJunk = (t) => /OPN|OPEN|オープン/i.test(t || ""); // オープン参加のクロス掲載（"W2- OPN"等）は除外
  const roundOf = {}; PROG_BUCKETS.forEach((b) => { roundOf[b.key] = b.round; });

  // クルー単位で到達状況をフラグで集約する。
  // ・決勝/準決/敗者戦のレーンに載っている＝そこへ到達（着順が未確定でもA決勝発艇表に載っていればA決勝）。
  //   karalは各組の1着艇にしか qualify（→Ａ決勝等）を付けないため、2着のタイム上がり進出はレーン掲載で拾う。
  // ・準決/敗者戦を「漕ぎ終えて」上位に進めなかったクルーは敗退（＝未発走のときだけ準決/敗者戦バケット）。
  // ・DNS（棄権・途中棄権）とオープンは進出の根拠にしない。
  const crews = {}; // evKey|normTeam -> flags
  RW.EVENTS.forEach((ev) => {
    const races = RW.realRaces(ev.key);
    if (!races) return;
    races.forEach((kr) => {
      const round = rl(kr.round);
      (kr.lanes || []).forEach((l) => {
        const team = l.team;
        if (!team || isJunk(team)) return;
        const key = ev.key + "|" + norm(team);
        if (!crews[key]) crews[key] = { evKey: ev.key, evName: ev.name, evType: ev.type, teamLabel: team,
          inA: false, inB: false, inSemi: false, racedSemi: false, inCr: false, racedCr: false,
          qA: false, qB: false, qS: false, qC: false, hasResult: false, dns: false };
        const c = crews[key];
        const q = zen(l.qualify);
        const isDns = /DNS|棄権/.test(q + zen(l.status));
        const isOpen = /オープン/.test(q);
        const raced = (l.rank != null) || !!l.status || isDns; // DNS/途中棄権もそのラウンドを消化済み扱い
        if (isDns) c.dns = true;
        if (raced) c.hasResult = true;
        if (isOpen) return;
        if (round === "A決勝") c.inA = true;                                     // 発艇表に掲載＝到達
        else if (round === "B決勝") c.inB = true;
        else if (round === "準決" || round === "準々決勝") { c.inSemi = true; if (raced) c.racedSemi = true; }
        else if (round === "敗者戦") { c.inCr = true; if (raced) c.racedCr = true; }
        if (!isDns) {                                                             // qualify（→行き先）。棄権の矢印は無効
          if (/A決勝/.test(q)) c.qA = true;
          else if (/B決勝/.test(q)) c.qB = true;
          else if (/準決/.test(q)) c.qS = true;
          else if (/敗者|敗復/.test(q)) c.qC = true;
        }
      });
    });
  });

  const teams = {};
  Object.keys(crews).forEach((key) => {
    const c = crews[key];
    let bucket;
    if (c.inA || c.qA) bucket = "afinal";                            // A決勝発艇表に掲載 or →Ａ決勝（タイム上がり含む）
    else if (c.inB || c.qB) bucket = "bfinal";
    else if (c.inSemi || c.qS) bucket = "semi";                      // 準決に到達（準決で敗退もここ＝予選落ちと区別。決勝進出は上のafinal/bfinal優先）
    else if ((c.inCr || c.qC) && !c.racedCr) bucket = "cr";          // これから敗者戦（漕いで敗退なら下のout）
    else if (c.hasResult || c.dns) bucket = "out";                  // 予選・敗者戦で敗退（準決未到達）＝敗退
    else return;                                                      // 未発走のみ＝数えない
    const base = RW.baseTeam(c.teamLabel);
    if (!teams[base]) teams[base] = { team: base, afinal: 0, semi: 0, bfinal: 0, cr: 0, out: 0, total: 0, crews: { afinal: [], semi: [], bfinal: [], cr: [], out: [] } };
    teams[base][bucket] += 1; teams[base].total += 1;
    // 行き先ラウンドの発艇時刻（例：A決勝バケット→そのクルーの種目のA決勝発艇時刻）
    const dr = roundOf[bucket];
    const race = dr ? RW.FLAT.find((r) => r.evKey === c.evKey && r.round === dr) : null;
    const suffix = (c.teamLabel && c.teamLabel !== base) ? c.teamLabel.slice(base.length).replace(/^[\s　]+/, "") : "";
    teams[base].crews[bucket].push({ evKey: c.evKey, evName: c.evName, evType: c.evType, teamLabel: c.teamLabel, suffix, time: race ? race.t : "", bucket });
  });
  // 各バケット内は発艇時刻順（時刻なしは後ろ）。"9:50"と"11:02"のように桁数が違うため分に直して比較する。
  const tmin = (s) => { if (!s) return 99999; const p = String(s).split(":"); return (+p[0]) * 60 + (+p[1] || 0); };
  Object.values(teams).forEach((t) => {
    Object.keys(t.crews).forEach((bk) => {
      t.crews[bk].sort((a, b) => tmin(a.time) - tmin(b.time) || a.evName.localeCompare(b.evName, "ja"));
    });
    // チーム名タップ用：全バケットのクルーをまとめて発艇時刻順に（種目一覧として一覧表示するため）。
    t.crews.all = PROG_BUCKETS.flatMap((b) => t.crews[b.key]).sort((a, b) => tmin(a.time) - tmin(b.time) || a.evName.localeCompare(b.evName, "ja"));
  });
  return Object.values(teams).sort((a, b) =>
    b.afinal - a.afinal || b.semi - a.semi || b.bfinal - a.bfinal || b.cr - a.cr || b.total - a.total || a.team.localeCompare(b.team, "ja"));
}

// あるチーム（base team）が、指定区分の確定した決勝で獲得したメダル内訳 [{evName, rank}]（金→銀→銅順）。
function teamMedals(teamBase, div, ord) {
  const RW = window.RW;
  const out = [];
  RW.EVENTS.forEach((ev) => {
    if (div && ev.div !== div) return;
    if (!ev.finalA || ord < ev.finalA.endOrd || !RW.realDecided(ev.key)) return;
    (RW.PODIUMS[ev.key] || []).forEach((p) => {
      if (p.team === teamBase) out.push({ evKey: ev.key, evName: ev.name, evType: ev.type, rank: p.rank, rawTeam: p.rawTeam || p.label });
    });
  });
  return out.sort((a, b) => a.rank - b.rank || a.evName.localeCompare(b.evName, "ja"));
}

function ResultsView({ ord, phase, onOpenTeam, goTab }) {
  const RW = window.RW;
  // 「時間が過ぎた」だけでは確定扱いにしない。karal.jpの実データで確定した決勝のみ「決定済み」。
  const decided = RW.FLAT.filter((r) => r.isFinalA && ord >= r.endOrd && RW.realDecided(r.ev.key));
  const awaitingFinals = RW.FLAT.filter((r) => r.isFinalA && ord >= r.endOrd && !RW.realDecided(r.ev.key));
  // メダル獲得者（確定した決勝）が出てからは「メダルランキング」を先頭＆既定にする。
  const hasMedals = decided.length > 0;
  const tabOrder = hasMedals ? ["medals", "prog", "finals"] : ["prog", "medals", "finals"];
  const TAB_LABEL = { prog: "チーム別 進出状況", medals: "メダルランキング", finals: "決勝結果" };
  const [tab, setTab] = React.useState(hasMedals ? "medals" : "prog");
  const [openBucket, setOpenBucket] = React.useState(null); // "team|bucket"
  const [openCrew, setOpenCrew] = React.useState(null);     // "team|bucket|idx"
  const [medalMode, setMedalMode] = React.useState("count"); // "count" | "people"
  const [openMedalTeam, setOpenMedalTeam] = React.useState(null); // "div|team"（メダル内訳の展開）
  const [openMedalCrew, setOpenMedalCrew] = React.useState(null); // "div|team|idx"（メダル種目の選手展開）
  const progRows = RW.hasReal() ? buildProgressTable() : [];
  const toggleBucket = (team, bk) => {
    const k = team + "|" + bk;
    setOpenCrew(null);
    setOpenBucket((prev) => (prev === k ? null : k));
  };

  if (phase === "before") {
    return (
      <section className="view">
        <div className="shead"><span className="tag"><span>RESULTS</span></span><span className="jp">結果・順位</span></div>
        <div className="empty">
          <IconMedal size={34} style={{ color: "var(--mut)", margin: "0 auto 10px", display: "block" }} />
          大会はまだ始まっていません。<br />結果は <b>7月4日(土)</b> のレース開始後に表示されます。
          <div style={{ marginTop: 14 }}><button className="chip" onClick={() => goTab("home")}>エントリー（種目一覧）を見る ›</button></div>
        </div>
      </section>
    );
  }

  return (
    <section className="view">
      <div className="shead"><span className="tag"><span>RESULTS</span></span><span className="jp">結果・順位</span><span className="sp" /><span className="meta">{decided.length}/{RW.COUNTS.finals} 決勝</span></div>
      <div className="chips">
        {tabOrder.map((k) => (
          <button key={k} className={"chip" + (tab === k ? " on" : "")} onClick={() => setTab(k)}>{TAB_LABEL[k]}</button>
        ))}
      </div>

      {tab === "prog" && (progRows.length ? (
        <div>
          <div className="pgt-hint"><b>チーム名を押す</b>と出場種目の一覧、<b>種目名を押す</b>と選手名、<b>選手名を押す</b>とプロフィールが見られます。チップは行き先（A決勝・準決など）で絞り込めます。</div>
          <div className="pgt">
            {progRows.map((t, i) => {
              const allKey = t.team + "|all";
              const allOpen = openBucket === allKey;
              return (
                <div className="pgt-item" key={t.team}>
                  <div className="pgt-row">
                    <span className="pgt-rk">{i + 1}</span>
                    <span className={"pgt-tm" + (allOpen ? " on" : "")} onClick={() => toggleBucket(t.team, "all")}>{t.team}<i>{t.total}クルー</i></span>
                    <span className="pgt-chips">
                      {PROG_BUCKETS.map((b) => t[b.key] > 0 ? (
                        <button type="button" key={b.key}
                          className={"progchip " + b.cls + (openBucket === t.team + "|" + b.key ? " on" : "")}
                          onClick={() => toggleBucket(t.team, b.key)}>{b.label} {t[b.key]}</button>
                      ) : null)}
                    </span>
                  </div>
                  {allOpen && (
                    <div className="pgt-drill">
                      {t.crews.all.map((c, ci) => {
                        const bDef = PROG_BUCKETS.find((b) => b.key === c.bucket);
                        const ckey = allKey + "|" + ci;
                        const on = openCrew === ckey;
                        const ath = on ? crewAthletes(c.evKey, c.teamLabel) : [];
                        return (
                          <div className="pgt-crew" key={ci}>
                            <div className="pgt-crewhd" onClick={() => setOpenCrew((prev) => (prev === ckey ? null : ckey))}>
                              {bDef && <span className={"pgt-btag " + bDef.cls}>{bDef.label}</span>}
                              <span className="pgt-cev">{c.evName}{c.suffix ? <i>{c.suffix}</i> : null}</span>
                              {c.time ? <span className="pgt-ctime">{c.time}</span> : null}
                              <IconChevron dir={on ? "up" : "down"} size={11} />
                            </div>
                            {on && (
                              <div className="pgt-ath">
                                {ath.length ? ath.map((m, mi) => (
                                  <span className="pgt-seat" key={mi}>{c.evType !== "scull" && <b>{m[0]}</b>}<RowerName name={m[1]} kana={m[2]} /></span>
                                )) : <span className="pgt-noath">選手名は準備中です</span>}
                              </div>
                            )}
                          </div>
                        );
                      })}
                      <button type="button" className="pgt-detlink" onClick={() => onOpenTeam(t.team)}>チーム詳細を見る ›</button>
                    </div>
                  )}
                  {PROG_BUCKETS.map((b) => (openBucket === t.team + "|" + b.key && t.crews[b.key].length) ? (
                    <div className="pgt-drill" key={b.key}>
                      {t.crews[b.key].map((c, ci) => {
                        const ckey = t.team + "|" + b.key + "|" + ci;
                        const on = openCrew === ckey;
                        const ath = on ? crewAthletes(c.evKey, c.teamLabel) : [];
                        return (
                          <div className="pgt-crew" key={ci}>
                            <div className="pgt-crewhd" onClick={() => setOpenCrew((prev) => (prev === ckey ? null : ckey))}>
                              <span className="pgt-cev">{c.evName}{c.suffix ? <i>{c.suffix}</i> : null}</span>
                              {c.time ? <span className="pgt-ctime">{c.time}</span> : null}
                              <IconChevron dir={on ? "up" : "down"} size={11} />
                            </div>
                            {on && (
                              <div className="pgt-ath">
                                {ath.length ? ath.map((m, mi) => (
                                  <span className="pgt-seat" key={mi}>{c.evType !== "scull" && <b>{m[0]}</b>}<RowerName name={m[1]} kana={m[2]} /></span>
                                )) : <span className="pgt-noath">選手名は準備中です</span>}
                              </div>
                            )}
                          </div>
                        );
                      })}
                    </div>
                  ) : null)}
                </div>
              );
            })}
          </div>
        </div>
      ) : <div className="empty">進出状況は、予選の結果（速報）が入り次第ここに表示されます。</div>)}

      {tab === "medals" && (() => {
        // 全日本ガイド風の「団体別メダル」横棒グラフ。一般/浜寺杯/マスターズの3区分に分けて表示。
        const DIVS = [
          { key: "general", label: "一般", tag: "" },
          { key: "hamadera", label: "浜寺杯", tag: "hm" },
          { key: "masters", label: "マスターズ", tag: "ms" },
        ];
        const secs = DIVS.map((d) => ({ d, rows: RW.medalTable(ord, d.key) })).filter((s) => s.rows.length);
        if (!secs.length) return <div className="empty">まだメダルは確定していません。</div>;
        const pick = (t) => medalMode === "people" ? [t.gp, t.sp, t.bp] : [t.g, t.s, t.b];
        return (
          <div>
            <div className="mtoggle">
              <button type="button" className={medalMode === "count" ? "on" : ""} onClick={() => setMedalMode("count")}>メダル数</button>
              <button type="button" className={medalMode === "people" ? "on" : ""} onClick={() => setMedalMode("people")}>人数</button>
            </div>
            <div className="mbt-all">
              {secs.map(({ d, rows }) => {
                const keyed = rows.map((t) => { const [g, s, b] = pick(t); return { team: t.team, g, s, b, tot: g + s + b }; })
                  .sort((a, b) => b.g - a.g || b.s - a.s || b.b - a.b || b.tot - a.tot || a.team.localeCompare(b.team, "ja"));
                const max = Math.max(1, ...keyed.map((t) => t.tot));
                return (
                  <div className="mbt-sec" key={d.key}>
                    <div className="mbt-divhd"><span className={"mbt-divtag" + (d.tag ? " " + d.tag : "")}>{d.label}</span><span className="n">{keyed.length}チーム</span></div>
                    <div className="mbt-chart">
                      {keyed.map((t, i) => {
                        const tie = i > 0 && t.g === keyed[i - 1].g && t.s === keyed[i - 1].s && t.b === keyed[i - 1].b;
                        const mkey = d.key + "|" + t.team;
                        const open = openMedalTeam === mkey;
                        const medals = open ? teamMedals(t.team, d.key, ord) : [];
                        return (
                          <div className={"mbt-item" + (open ? " open" : "")} key={t.team}>
                            <div className="mbt-row" onClick={() => { setOpenMedalCrew(null); setOpenMedalTeam((prev) => (prev === mkey ? null : mkey)); }}>
                              <span className="mbt-rk">{tie ? "=" : (i + 1)}</span>
                              <span className="mbt-tm">{t.team}</span>
                              <span className="mbt-barwrap">
                                <span className="mbt-bar">
                                  {/* 色帯全体を mbt-fill で包み、1本のシャインが金→銀→銅の順に走り抜ける */}
                                  <span className="mbt-fill" style={{ width: (t.tot / max * 100) + "%" }}>
                                    {t.g > 0 && <span className="mbt-seg g" style={{ width: (t.g / t.tot * 100) + "%" }} />}
                                    {t.s > 0 && <span className="mbt-seg s" style={{ width: (t.s / t.tot * 100) + "%" }} />}
                                    {t.b > 0 && <span className="mbt-seg b" style={{ width: (t.b / t.tot * 100) + "%" }} />}
                                  </span>
                                </span>
                                <span className="mbt-val">
                                  {t.g > 0 && <span className="mc g"><i />{t.g}</span>}
                                  {t.s > 0 && <span className="mc s"><i />{t.s}</span>}
                                  {t.b > 0 && <span className="mc b"><i />{t.b}</span>}
                                </span>
                              </span>
                            </div>
                            {open && (
                              <div className="mbt-detail">
                                {medals.length ? medals.map((m, mi) => {
                                  const ckey = mkey + "|" + mi;
                                  const cOpen = openMedalCrew === ckey;
                                  const ath = cOpen ? crewAthletes(m.evKey, m.rawTeam) : [];
                                  return (
                                    <div className="mbt-medal-wrap" key={mi}>
                                      <div className="mbt-medal" onClick={(e) => { e.stopPropagation(); setOpenMedalCrew((prev) => (prev === ckey ? null : ckey)); }}>
                                        <span className={"mbt-md " + (m.rank === 1 ? "g" : m.rank === 2 ? "s" : "b")} />
                                        <span className="mbt-mev">{m.evName}</span>
                                        <span className="mbt-mrk">{m.rank === 1 ? "金" : m.rank === 2 ? "銀" : "銅"}</span>
                                        <IconChevron dir={cOpen ? "up" : "down"} size={11} />
                                      </div>
                                      {cOpen && (
                                        <div className="mbt-math">
                                          {ath.length ? ath.map((seat, si) => (
                                            <span className="pgt-seat" key={si}>{m.evType !== "scull" && <b>{seat[0]}</b>}<RowerName name={seat[1]} kana={seat[2]} /></span>
                                          )) : <span className="pgt-noath">選手名は準備中です</span>}
                                        </div>
                                      )}
                                    </div>
                                  );
                                }) : <div className="mbt-medal"><span className="mbt-mev" style={{ color: "var(--mut)" }}>内訳は準備中です</span></div>}
                                <button type="button" className="mbt-detlink" onClick={(e) => { e.stopPropagation(); onOpenTeam(t.team); }}>チーム詳細を見る ›</button>
                              </div>
                            )}
                          </div>
                        );
                      })}
                    </div>
                  </div>
                );
              })}
            </div>
            <p className="navsrc" style={{ marginTop: 6 }}>{medalMode === "people" ? "人数＝金・銀・銅を獲得したクルーの乗艇人数（舵手含む）。" : "各区分の金・銀・銅メダル数。"}バーは区分内の最多を基準に表示。</p>
          </div>
        );
      })()}

      {tab === "finals" && (
        <div>
          {decided.map((fb) => (
            <div className="medalcard" key={fb.ev.key}>
              <div className="mh"><Code ev={fb.ev} /><DivChip div={fb.ev.div} /><span className="nm2">{fb.ev.name}</span><span className="when">{fb.dow} {fb.t}</span></div>
              <PodiumRows podium={RW.PODIUMS[fb.ev.key]} onOpenTeam={onOpenTeam} />
            </div>
          ))}
          {awaitingFinals.map((fb) => (
            <div className="medalcard" key={fb.ev.key}>
              <div className="mh"><Code ev={fb.ev} /><DivChip div={fb.ev.div} /><span className="nm2">{fb.ev.name}</span><span className="when">{fb.dow} {fb.t}</span></div>
              <div className="rc-res-wait">結果待ち（速報反映まで数分かかることがあります）</div>
            </div>
          ))}
          {!decided.length && !awaitingFinals.length && <div className="empty">確定した決勝はまだありません。</div>}
          {phase === "during" && (
            <div style={{ textAlign: "center", marginTop: 12, fontSize: 11.5, color: "var(--mut)" }}>※ 未実施の決勝はレース終了後に順次表示されます</div>
          )}
        </div>
      )}
    </section>
  );
}
Object.assign(window, { ResultsView });
