/* teams.jsx — 団体一覧（大会別＋五十音の行見出し）・検索・お気に入り・詳細
   TEAM_YOMI は並べ替え用の読み（102団体ぶん）。長音符は入れず、拗音・促音は小書きにしない。 */
// 五十音ソート用の読み（baseTeam正規化後の団体名→ひらがな）
const TEAM_YOMI = {
 "青山学院大学":"あおやまがくいん", "ＮＴＴ東日本":"えぬていていひがしにほん", "愛媛大学":"えひめ", "追手門学院大学":"おうてもんがくいん", "大阪大学":"おおさか",
 "大阪・大阪公立混成":"おおさかおおさかこうりつこんせい", "大阪工業大学":"おおさかこうぎよう", "大阪公立大学":"おおさかこうりつ", "岡山大学":"おかやま", "小樽商科大学":"おたるしようか",
 "鹿児島大学":"かごしま", "金沢大学":"かなざわ", "鹿屋体育大学":"かのやたいいく", "関西大学":"かんさい", "関西学院大学":"かんせいがくいん",
 "関西学院・関西混成":"かんせいがくいんかんさいこんせい", "学習院大学":"がくしゆういん", "北里大学医学部":"きたさといがくぶ", "九州大学":"きゆうしゆう", "京都大学":"きようと",
 "京都米子JDS混成":"きようとよなごじえいでいえすこんせい", "岐阜大学":"ぎふ", "熊本大学":"くまもと", "熊本学園大学":"くまもとがくえん", "慶應義塾大学":"けいおうぎじゆく",
 "慶應一橋混成":"けいおうひとつばしこんせい", "警視庁":"けいしちよう", "高知大学":"こうち", "神戸大学":"こうべ", "滋賀大学":"しが", "滋賀医科大学":"しがいか",
 "滋賀大学教育学部":"しがきよういくがくぶ", "志水会混成":"しみずかいこんせい", "周南公立大学":"しゆうなんこうりつ", "自治医科大学":"じちいか", "成蹊大学":"せいけい", "成城大学":"せいじよう",
 "清風「葛」会":"せいふうかつらかい", "瀬田漕艇クラブ混成":"せたそうていくらぶこんせい", "仙台大学":"せんだい", "総合研究大学院大学":"そうごうけんきゆうだいがくいん",
 "WRインビクタス":"だぶりゆあるいんびくたす", "千葉大学":"ちば", "中央大学":"ちゆうおう", "中央大学理工学部":"ちゆうおうりこうがくぶ", "中部電力":"ちゆうぶでんりよく",
 "中部電力混成":"ちゆうぶでんりよくこんせい", "直進行軍":"ちよくしんこうぐん", "筑波大学":"つくば", "DRC混成":"でいあるしいこんせい", "デンソーオルカリス":"でんそおるかりす",
 "東海大学":"とうかい", "東京大学":"とうきよう", "東京科学大学医歯学系":"とうきようかがくいしがくけい", "東京科学大学理工学系":"とうきようかがくりこうがくけい",
 "東京外国語大学":"とうきようがいこくご", "東京経済大学":"とうきようけいざい", "東京都立大学":"とうきようとりつ", "東経大・明治大混成":"とうけいだいめいじだいこんせい", "同志社大学":"とうししや",
 "東大滋賀東北北大混成":"とうだいしがとうほくほくだいこんせい", "東大立教成城混成":"とうだいりつきようせいじようこんせい", "東北大学":"とうほく", "東北学院大学":"とうほくがくいん",
 "東北大学混成":"とうほくこんせい", "東北太陽愛知混成":"とうほくたいようあいちこんせい", "富山国際大学":"とやまこくさい", "トヨタ自動車RC":"とよたじどうしや", "名古屋大学":"なごや",
 "名古屋工業大学":"なごやこうぎよう", "名古屋大南山大混成":"なごやだいなんざんだいこんせい", "南山大学":"なんざん", "新潟大学":"にいがた", "新潟RC混成":"にいがたあるしいこんせい",
 "日本大学":"にほん", "日本体育大学":"にほんたいいく", "葉月RC混成":"はづきあるしいこんせい", "一橋大学":"ひとつばし", "広島大学":"ひろしま", "福岡教育大学":"ふくおかきよういく",
 "福岡女子大学":"ふくおかじよし", "福島県立医科大学":"ふくしまけんりついか", "法政大学":"ほうせい", "北海道大学":"ほつかいどう", "北海道大学医学部":"ほつかいどういがくぶ",
 "北海道大学水産学部":"ほつかいどうすいさんがくぶ", "防衛大学校":"ぼうえい", "松江工業高等専門学校":"まつえこうぎよう", "松山大学":"まつやま", "みちのくRC混成":"みちのくあるしいこんせい",
 "明治大学":"めいじ", "山茨福日医早理広混成":"やまいばらふくにちいそうりひろこんせい", "山形大学混成":"やまがたこんせい", "山口大学":"やまぐち", "山梨大学医学部":"やまなしいがくぶ",
 "横浜今治愛知東郷混成":"よこはまいまばりあいちとうごうこんせい", "立教大学":"りつきよう", "立命館大学":"りつめいかん", "龍谷大学":"りゆうこく", "早稲田大学":"わせだ",
 "早稲田大学理工漕艇部":"わせだりこうそうていぶ", "早稲田理工碧水会混成":"わせだりこうへきすいかいこんせい",
};
const byYomi = (a, b) => (TEAM_YOMI[a] || a).localeCompare(TEAM_YOMI[b] || b, "ja");
// ラウンド種別→色分けキー。決勝に近いほど鮮やかな配色になるよう段階づけ
const roundKind = (r) => r.isFinalA ? "afinal" : r.round === "B決勝" ? "bfinal" : r.round === "準決" ? "semi" : r.round === "敗者戦" ? "cr" : "heat";
// 進出チップの色は「行き先」に合わせる（準決=紫 / A決勝=金 / B決勝=黄 / 敗者戦=グレー / それ以外=緑）。日程セルの配色と統一。
// karal.jpは「→Ａ決勝」など全角英字で配信するため、判定前に半角へ正規化する。
const zenQ = (s) => String(s || "").replace(/[Ａ-Ｚａ-ｚ０-９]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xFEE0));
const progDest = (qRaw) => { const q = zenQ(qRaw); return !q ? "" : /A決勝/.test(q) ? "prog-afinal" : /B決勝/.test(q) ? "prog-bfinal" : /準決/.test(q) ? "prog-semi" : /敗者|敗復/.test(q) ? "prog-cr" : /DNS|棄権/.test(q) ? "" : "go"; };
function TeamDetail({ team, ord, isFav, toggleFav, onClose, onOpenEvent }) {
  const RW = window.RW;
  const ROS = window.KANSAI.ROSTERS || {};
  const minorTeam = RW.isMinorTeam && RW.isMinorTeam(team);  // 高校（未成年）は氏名を出さない
  const t = RW.TEAMS[team];
  const [openK, setOpenK] = React.useState(null);
  React.useEffect(() => { setOpenK(null); }, [team]);
  if (!t) return null;
  // チーム詳細内では基準チーム名は自明なので、クルー表記から団体名を落として A/B/C だけ見せる
  // （例：「同志社大学A」→「A」）。接頭辞が一致しない稀なケースは元の表記をそのまま返す。
  const crewSuffix = (label) => {
    const s = String(label || "").trim();
    if (team && s.startsWith(team)) { const rest = s.slice(team.length).replace(/^[\s　・]+/, ""); return rest || s; }
    return s;
  };
  // group crews by event
  const byEvent = {};
  t.crews.forEach((c) => { (byEvent[c.evKey] = byEvent[c.evKey] || []).push(c); });
  const evKeys = Object.keys(byEvent).sort((a, b) => RW.evOf[a].div.localeCompare(RW.evOf[b].div));
  const finalsWon = evKeys.filter((k) => RW.evOf[k].finalA && ord >= RW.evOf[k].finalA.endOrd);
  const prog = RW.favProgress(team);
  const isAfter = ord >= RW.LAST; // 大会後：日程マスをやめ「最終結果」を出す
  const zenR = (s) => String(s || "").replace(/[Ａ-Ｚａ-ｚ０-９]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xFEE0));
  // クルーの到達最終ラウンド＋着順から「最終結果」を作る。決勝は着順を“総合順位”に変換して表示。
  //   今大会は6艇立て（A決勝＝総合1〜6位）。A決勝: 1/2/3=メダル、4〜6=そのまま「N位」。
  //   B決勝: 総合 = 6 + 着順（例 B決勝2位→総合8位）、C決勝: 12 + 着順。「A決勝/B決勝」表記は出さない。
  //   決勝以外（準決・敗者戦・予選）は従来どおり「◯◯敗退」。
  const FINAL_LANES = 6;
  const resultOf = (p) => {
    const q = zenR(p.qualify), r = p.rank, R = p.lastRound;
    if (r == null && /DNS|棄権/.test(q)) return { text: "棄権", cls: "out" };
    if (R === "A決勝") {
      if (r === 1) return { medal: "🥇", text: "優勝", cls: "gold" };
      if (r === 2) return { medal: "🥈", text: "2位", cls: "silver" };
      if (r === 3) return { medal: "🥉", text: "3位", cls: "bronze" };
      return { text: r != null ? r + "位" : "決勝進出", cls: "af" };
    }
    if (R === "B決勝") return { text: r != null ? "総合" + (FINAL_LANES + r) + "位" : "出場", cls: "bf" };
    if (R === "C決勝") return { text: r != null ? "総合" + (FINAL_LANES * 2 + r) + "位" : "出場", cls: "bf" };
    if (R === "準決") return { text: "準決敗退", cls: "sf" };
    if (R === "敗者戦") return { text: "敗者戦敗退", cls: "cr" };
    return { text: (R || "予選") + "敗退", cls: "out" };
  };
  // 土日×午前午後の4マスに、その種目のラウンドを時刻で振り分け（無いマスは「ー」）
  const DOW = [0, 1].map((di) => (RW.FLAT.find((r) => r.dayIndex === di) || {}).dow || (di ? "日" : "土"));
  const scheduleCells = (evKey) => {
    const rounds = RW.FLAT.filter((x) => x.ev.key === evKey);
    return [0, 1].flatMap((di) => [0, 1].map((half) => {
      const items = rounds.filter((r) => r.dayIndex === di && (r.startMin >= 720) === !!half);
      return {
        day: di, dow: DOW[di], ampm: half ? "午後" : "午前",
        rows: items.map((r) => ({ kind: roundKind(r), t: r.t, label: r.isFinalA ? "A決勝" : r.round })),
      };
    }));
  };

  return (
    <div className="tcard">
      <div className="th" onClick={() => onClose && onClose()} style={{ cursor: onClose ? "pointer" : "default" }}>
        <span className="lanes" />
        <div className="thbtns">
          <StarBtn on={isFav} onClick={() => toggleFav(team)} size={24} />
          {onClose && <button className="closebtn" aria-label="閉じる" onClick={(e) => { e.stopPropagation(); onClose(); }}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M6 6l12 12M18 6L6 18" /></svg></button>}
        </div>
        <h3>{team}</h3>
        <div className="meta"><span>{evKeys.length}種目 · {t.crews.length}エントリー</span></div>
      </div>
      <div className="tbody">
        {evKeys.map((k) => {
          const ev = RW.evOf[k];
          const crews = byEvent[k];
          const ros = ROS[k] || {};
          const op = openK === k;
          const cells = scheduleCells(k);
          const evProg = prog.filter((p) => p.evKey === k);
          return (
            <React.Fragment key={k}>
              <div className="trow clk" onClick={() => setOpenK(op ? null : k)} style={{ cursor: "pointer" }}>
                <span className="tcode"><Code ev={ev} /></span>
                <span className="tnm">{ev.name}{crews.length > 1 ? `（${crews.length}）` : ""}{ev.div !== "general" ? " · " + RW.DIV[ev.div].label : ""}</span>
                <IconChevron dir={op ? "up" : "down"} size={12} />
              </div>
              {isAfter ? (
                /* 大会後：種目ごとの最終結果（決勝はメダル・着順、他は「◯◯敗退」） */
                <div className="tresrow">
                  <span className="treslbl">最終結果</span>
                  {evProg.length ? evProg.map((p, i) => { const R = resultOf(p); return (
                    <span className={"tres " + R.cls} key={i}>
                      {crews.length > 1 && <b className="trescrew">{crewSuffix(p.crewLabel)}</b>}
                      {R.medal && <em className="tresmedal">{R.medal}</em>}{R.text}
                    </span>
                  ); }) : <span className="tres out">結果は結果タブへ</span>}
                </div>
              ) : (
                <React.Fragment>
                  {evProg.length > 0 && (
                    <div style={{ display: "flex", flexWrap: "wrap", gap: 5, margin: "-2px 0 8px" }}>
                      {evProg.map((p, i) => (
                        <span className={"progchip " + progDest(p.qualify)} key={i}>
                          {crews.length > 1 ? crewSuffix(p.crewLabel) + " " : ""}{p.lastRound}{p.grp ? " " + p.grp : ""}{p.rank != null ? " " + p.rank + "位" : ""}{p.qualify ? " " + p.qualify : ""}
                        </span>
                      ))}
                    </div>
                  )}
                  <div className="tsched">
                    {cells.map((c, i) => (
                      <div className={"tsc" + (c.rows.length ? "" : " norace")} key={i}>
                        <div className="tsc-h"><span className={"tsc-d " + (c.day ? "sun" : "sat")}>{c.dow}</span>{c.ampm}</div>
                        {c.rows.length
                          ? c.rows.map((it, j) => (
                              <div className={"tsc-r " + it.kind} key={j}><i>{it.label}</i><b>{it.t}</b></div>
                            ))
                          : <div className="tsc-none">ー</div>}
                      </div>
                    ))}
                  </div>
                </React.Fragment>
              )}
              {op && (
                <div className="trow-ros">
                  {crews.map((c) => {
                    const ms = ros[c.n] || [];
                    return (
                      <div className={"trow-crew" + (c.x ? " scratch" : "")} key={c.n}>
                        {crews.length > 1 && <div className="trow-crewlbl">{crewSuffix(c.label)}{c.x && <span className="rc-dnf" style={{ marginLeft: 6 }}>棄権</span>}</div>}
                        {crews.length === 1 && c.x && <div className="trow-crewlbl"><span className="rc-dnf">棄権</span></div>}
                        {(ms.length && !minorTeam) ? ms.map((m, i) => (
                          <div className="trow-seat" key={i}>
                            {ev.type !== "scull" && <span className="trow-s">{m[0]}</span>}
                            <span className="trow-nm"><RowerName name={m[1]} kana={m[2]} /></span>
                          </div>
                        )) : <div className="trow-seat"><span className="trow-nm">{minorTeam ? c.label : (c.name || c.label)}</span></div>}
                      </div>
                    );
                  })}
                  <button type="button" className="trow-evbtn" onClick={(e) => { e.stopPropagation(); onOpenEvent(k); }}>種目の全クルーを見る →</button>
                </div>
              )}
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
}

function TeamsView({ ord, favTeams, toggleFav, selectedTeam, setSelectedTeam, onOpenEvent, q, setQ }) {
  const RW = window.RW;
  const ROS = window.KANSAI.ROSTERS || {};
  // 団体の総人数（各クルーの名簿人数、無ければ艇の定員で概算）
  const teamHeads = (t) => RW.TEAMS[t].crews.reduce((n, c) => {
    const r = (ROS[c.evKey] || {})[c.n];
    return n + (r && r.length ? r.length : RW.seatsOf(RW.evOf[c.evKey]));
  }, 0);
  // 名簿が未公表の間は「艇の定員×クルー数＝延べ座席数」しか出せない。
  // 1人が複数種目に乗るので実人数より多くなる → ラベルで「延べ」と明示する。
  const HAS_ROSTER = Object.keys(ROS).length > 0;
  const [sortMode, setSortMode] = React.useState("yomi"); // "yomi"=五十音順 / "heads"=出場者数（多い順）
  const cmp = sortMode === "heads" ? (a, b) => teamHeads(b) - teamHeads(a) || byYomi(a, b) : byYomi;
  const all = RW.TEAM_NAMES;
  const favList = all.filter((t) => favTeams[t]).slice().sort(byYomi);
  const filtered = all.filter((t) => !q || t.includes(q));
  const athleteHits = q ? RW.searchAthletes(q).slice(0, 6) : [];
  const sorted = filtered.slice().sort(cmp);

  // ── 団体を「出場する大会（部門）」で分ける。1団体が複数大会に出ることもある（両方に載せる）。
  const DIV_ORDER = Object.keys(RW.DIV).sort((a, b) => RW.DIV[a].order - RW.DIV[b].order);
  const divsOfTeam = (t) => {
    const set = new Set();
    (RW.TEAMS[t] ? RW.TEAMS[t].crews : []).forEach((c) => { const e = RW.evOf[c.evKey]; if (e) set.add(e.div); });
    return set;
  };
  // ── 五十音の「行」（あ/か/さ…）。濁音・半濁音は清音の行に寄せる。
  const KANA_ROWS = [
    ["あ", "あいうえおぁぃぅぇぉ"],
    ["か", "かきくけこがぎぐげご"],
    ["さ", "さしすせそざじずぜぞ"],
    ["た", "たちつてとだぢづでどっ"],
    ["な", "なにぬねの"],
    ["は", "はひふへほばびぶべぼぱぴぷぺぽ"],
    ["ま", "まみむめも"],
    ["や", "やゆよゃゅょ"],
    ["ら", "らりるれろ"],
    ["わ", "わをん"],
  ];
  const rowOf = (t) => {
    const y = TEAM_YOMI[t] || t;
    const c = y.charAt(0);
    const hit = KANA_ROWS.find(([, chars]) => chars.indexOf(c) >= 0);
    return hit ? hit[0] : "他";
  };

  const TeamBtn = ({ t }) => {
    const T = RW.TEAMS[t];
    return (
      <button className="tbtn" onClick={() => setSelectedTeam(t === selectedTeam ? null : t)}>
        {favTeams[t] && <span className="fav-s"><Star size={11} /></span>}
        <span className="tbtn-nm">{t}</span>
        <span className="tbtn-stats">
          <span className="ts-x ev"><b>{T.evKeys.size}</b>種目</span>
          <span className="ts-x cr"><b>{T.crews.length}</b>クルー</span>
          <span className="ts-x pp"><b>{teamHeads(t)}</b>{HAS_ROSTER ? "名" : "名 延べ"}</span>
        </span>
      </button>
    );
  };
  // 部門ブロック：中を「あ行/か行…」で小見出し分け（五十音順のときだけ）。
  const DivGroup = ({ divKey, list }) => {
    if (!list.length) return null;
    const D = RW.DIV[divKey];
    const rows = [];
    if (sortMode === "yomi") {
      const order = KANA_ROWS.map(([r]) => r).concat("他");
      order.forEach((r) => {
        const items = list.filter((t) => rowOf(t) === r);
        if (items.length) rows.push([r, items]);
      });
    } else {
      rows.push([null, list]);
    }
    return (
      <div className="tgroup">
        <div className="tgroup-h">{D ? D.label : divKey}<span className="c">{list.length}</span></div>
        {rows.map(([r, items]) => (
          <div key={r || "all"}>
            {r && <div className="krow-h">{r}</div>}
            <div className="tlist">{items.map((t) => <TeamBtn key={divKey + t} t={t} />)}</div>
          </div>
        ))}
      </div>
    );
  };

  return (
    <section className="view">
      <div className="shead"><span className="tag"><span>TEAMS</span></span><span className="jp">チームから探す</span><span className="sp" /><span className="meta">{all.length}団体</span></div>
      <div className="search"><IconSearch size={20} /><input value={q} onChange={(e) => setQ(e.target.value)} placeholder="団体名で検索（例：同志社、京都、瀬田…）" autoComplete="off" /></div>
      {selectedTeam && <TeamDetail team={selectedTeam} ord={ord} isFav={!!favTeams[selectedTeam]} toggleFav={toggleFav} onClose={() => setSelectedTeam(null)} onOpenEvent={onOpenEvent} />}
      {!q && favList.length > 0 && (
        <div className="favsec" style={{ marginTop: 14 }}>
          <div className="favtitle"><Star size={14} color="var(--red)" />お気に入り</div>
          <div className="tlist">{favList.map((t) => <TeamBtn key={t} t={t} />)}</div>
        </div>
      )}
      {q && athleteHits.length > 0 && (
        <div className="favsec" style={{ marginTop: 14 }}>
          <div className="favtitle">選手</div>
          <div className="nextlist">
            {athleteHits.map((a, i) => (
              <div className="nextrow" key={i} style={{ cursor: "pointer" }} onClick={() => setSelectedTeam(RW.baseTeam(a.team))}>
                <div className="nx" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 800, fontSize: 13 }}>{a.name}{a.kana && <i className="rn-kana" style={{ marginLeft: 6 }}>{a.kana}</i>}</div>
                    <div className="nm">{a.team}・{a.evs.join(" / ")}</div>
                  </div>
                  <IconArrow size={16} />
                </div>
              </div>
            ))}
          </div>
        </div>
      )}
      {filtered.length > 0 && (
        <div className="team-sort">
          <span className="team-sort-lbl">並び替え</span>
          <button type="button" className={"chip" + (sortMode === "yomi" ? " on" : "")} onClick={() => setSortMode("yomi")}>五十音順</button>
          <button type="button" className={"chip" + (sortMode === "heads" ? " on" : "")} onClick={() => setSortMode("heads")}>出場者数</button>
        </div>
      )}
      <div style={{ marginTop: 10 }}>
        {filtered.length
          ? DIV_ORDER.map((dk) => <DivGroup key={dk} divKey={dk} list={sorted.filter((t) => divsOfTeam(t).has(dk))} />)
          : <div className="empty">「{q}」に一致する団体はありません</div>}
      </div>
    </section>
  );
}
Object.assign(window, { TeamsView });
