// OGS — 運営実績ストア一覧。LP（Production）と同じ視覚言語。
// データは CMS（microCMS 想定）から取得する前提のモック。
// STORES_CONFIG.source を切り替えると本番データを読みに行く。
//   'mock'     … モックデータ（現状）
//   'proxy'    … Vercel の /api/stores 経由（本番推奨 — API キー非公開）
//   'microcms' … microCMS を直接 fetch（検証用 — GET 専用キーが公開される）

/* ============================================================
   データ層 — CMS つなぎこみ
   ------------------------------------------------------------
   microCMS 側のセットアップ:
     1. リスト形式の API を作成 — エンドポイント名: stores
     2. フィールド:
          name        (テキストフィールド)  … ストア名
          url         (テキストフィールド)  … 遷移先 URL
          isActive    (真偽値フィールド)     … true=リンク表示 / false=テキストのみ（CLOSED）
          isVisible   (真偽値フィールド)     … false で一覧から非表示（オープン前など）。未設定/true は表示
          nameReading (テキストフィールド)  … 並び替え用の読みがな（任意）。
                                              漢字始まりの店舗にカタカナ/ひらがなの読みを入れると
                                              五十音順に並ぶ。未入力なら name で照合（下記ソート参照）
     3. 「APIキー」から GET 権限のみのキーを発行して下に設定
   ============================================================ */

const STORES_CONFIG = {
  source: 'proxy', // 'mock' | 'proxy' | 'microcms'　※本番デプロイ時は 'proxy' に
  proxy: {
    path: '/api/stores', // Vercel Functions — api/stores.js をデプロイ
  },
  microcms: {
    serviceDomain: 'reni', // https://reni.microcms.io
    apiKey: '', // GET 専用キー
    endpoint: 'stores',
  },
};

// 漢字始まりの店舗の読みがな（モック用デモ）。本番は microCMS の nameReading で管理。
const MOCK_READINGS = {
  優里: 'ゆうり',
  初音ミク: 'はつねみく',
  長渕剛: 'ながぶちつよし',
  堂本剛: 'どうもとつよし',
};

// モックデータ — LP 掲載の 30 組。CMS 投入後は使われない。
const MOCK_STORES = OGS.artists.map((name, i) => ({
  id: 'mock-' + i,
  name,
  nameReading: MOCK_READINGS[name], // 読みがあれば五十音ソートに使われる（任意）
  url: 'https://official-goods-store.jp/',
  isActive: i % 4 !== 0, // 動作確認用に一部を非アクティブ（リンクなし）に
  isVisible: i % 7 !== 0, // 動作確認用に一部を非表示に
}));

async function fetchStores() {
  if (STORES_CONFIG.source === 'proxy') {
    // API キーは Vercel の環境変数にあり、ブラウザには一切出ない
    const res = await fetch(STORES_CONFIG.proxy.path);
    if (!res.ok) throw new Error('API error: ' + res.status);
    return (await res.json()).contents;
  }
  if (STORES_CONFIG.source !== 'microcms') {
    return MOCK_STORES;
  }
  const { serviceDomain, apiKey, endpoint } = STORES_CONFIG.microcms;
  const all = [];
  let offset = 0;
  // microCMS の limit 上限は 100 — 400 件超でも全件取れるようページング
  for (; ;) {
    const res = await fetch(
      `https://${serviceDomain}.microcms.io/api/v1/${endpoint}` +
      `?limit=100&offset=${offset}&fields=id,name,nameReading,url,isActive,isVisible`,
      { headers: { 'X-MICROCMS-API-KEY': apiKey } }
    );
    if (!res.ok) throw new Error('microCMS error: ' + res.status);
    const data = await res.json();
    all.push(...data.contents);
    offset += data.contents.length;
    if (data.contents.length === 0 || offset >= data.totalCount) break;
  }
  return all;
}

/* ============================================================ */

// 並び替え用の照合器。日本語ロケールで大小・かな種別を無視し、
// numeric で数字を数値として扱う（NMB48 < NMB100）。
const STORE_COLLATOR = new Intl.Collator('ja', { numeric: true, sensitivity: 'base' });
// 照合キー — 読みがな(nameReading)があれば優先、無ければ name。
// これにより漢字始まりの店舗も、読みを入れれば五十音順に並ぶ。
const storeSortKey = (s) => (s.nameReading && s.nameReading.trim()) || s.name;

// 先頭文字でブロックを分類: 0=英数字 → 1=記号 → 2=日本語(かな・漢字)。
// 記号始まり（_ [ " など）を A–Z の後・日本語の前に置くための前段キー。
function storeBlock(s) {
  const ch = (storeSortKey(s).trim() || ' ').codePointAt(0);
  const isAlnum =
    (ch >= 0x30 && ch <= 0x39) || (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a) || // 半角 0-9 A-Z a-z
    (ch >= 0xff10 && ch <= 0xff19) || (ch >= 0xff21 && ch <= 0xff3a) || (ch >= 0xff41 && ch <= 0xff5a); // 全角 ０-９ Ａ-Ｚ
  const isJa =
    (ch >= 0x3005 && ch <= 0x3007) ||  // 々 〆 〇
    (ch >= 0x3040 && ch <= 0x30ff) ||  // ひらがな・カタカナ
    (ch >= 0x3400 && ch <= 0x9fff) ||  // CJK統合漢字（拡張A含む）
    (ch >= 0xf900 && ch <= 0xfaff) ||  // CJK互換漢字
    (ch >= 0xff66 && ch <= 0xff9f);    // 半角カタカナ
  if (isAlnum) return 0;
  if (isJa) return 2;
  return 1; // 記号その他
}

const VS_CSS = `
.vs-root {
  --paper: #FFFFFF;
  --ink: #0B0B09;
  --red: oklch(0.56 0.22 29);
  --gray: #56544E;
  --gray2: #8C897F;
  width: 1280px; min-height: 100vh; background: var(--paper); color: var(--ink);
  font-family: 'Zen Kaku Gothic New', sans-serif;
  -webkit-font-smoothing: antialiased;
}
.vs-root .cond { font-family: 'Archivo', 'Helvetica Neue', sans-serif; font-stretch: 62.5%; }
.vs-root .mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
.vs-pad { padding-left: 64px; padding-right: 64px; }

/* header — LP と同一 */
.vs-header { position: sticky; top: 0; z-index: 100; background: var(--paper); display: flex; align-items: center; justify-content: space-between; height: 70px; border-bottom: 3px solid var(--ink); }
.vs-wordmark { display: flex; align-items: baseline; gap: 14px; color: inherit; text-decoration: none; }
.vs-wordmark .mk { font-size: 32px; font-weight: 800; text-transform: uppercase; }
.vs-wordmark .sub { font-size: 10px; letter-spacing: 0.18em; color: var(--gray); }
.vs-nav { display: flex; align-items: center; gap: 30px; }
.vs-nav a { font-size: 11px; letter-spacing: 0.14em; color: var(--ink); text-decoration: none; font-weight: 500; }
.vs-nav a.vs-cta { display: inline-flex; align-items: center; gap: 10px; background: var(--red); color: #fff; padding: 12px 22px; font-size: 12px; letter-spacing: 0.1em; font-weight: 700; }

/* title */
.vs-strip { display: flex; justify-content: space-between; font-size: 11px; letter-spacing: 0.22em; padding: 22px 0 30px; font-weight: 500; }
.vs-display { font-weight: 800; font-size: 168px; line-height: 0.9; letter-spacing: -0.01em; text-transform: uppercase; margin: 0 0 0 -6px; }
.vs-lede { display: flex; justify-content: space-between; align-items: flex-end; gap: 40px; padding: 34px 0 26px; }
.vs-lede p { font-size: 14px; line-height: 2.0; color: var(--gray); margin: 0; max-width: 560px; }
.vs-count { font-size: 11px; letter-spacing: 0.18em; color: var(--gray2); white-space: nowrap; }
.vs-count b { color: var(--red); font-weight: 700; }

/* search */
.vs-toolbar { display: flex; align-items: center; gap: 20px; border-top: 3px solid var(--ink); padding: 18px 0; }
.vs-toolbar label { font-size: 11px; letter-spacing: 0.18em; color: var(--gray2); flex-shrink: 0; }
.vs-toolbar input { flex: 1; border: none; outline: none; background: transparent; font-family: 'Zen Kaku Gothic New', sans-serif; font-size: 16px; font-weight: 700; color: var(--ink); padding: 4px 0; }
.vs-toolbar input::placeholder { color: var(--gray2); font-weight: 400; }

/* list */
.vs-list { display: grid; grid-template-columns: 1fr 1fr; column-gap: 64px; border-top: 1px solid var(--ink); margin-bottom: 100px; }
.vs-row { display: flex; align-items: center; gap: 18px; padding: 17px 0; border-bottom: 1px solid rgba(11,11,9,0.18); color: inherit; text-decoration: none; }
.vs-row .idx { font-size: 10px; letter-spacing: 0.1em; color: var(--gray2); width: 34px; flex-shrink: 0; }
.vs-row .name { font-size: 18px; font-weight: 700; line-height: 1.4; }
.vs-row svg { margin-left: auto; flex-shrink: 0; color: var(--gray2); opacity: 0; transition: opacity 0.12s; }
.vs-row:hover .name { color: var(--red); }
.vs-row:hover svg { opacity: 1; color: var(--red); }
/* 非アクティブ（リンクなし）— 淡色テキスト＋CLOSEDタグ。ホバー効果なし */
.vs-row--static { cursor: default; }
.vs-row--static .name { color: var(--gray2); }
.vs-row .vs-tag { margin-left: auto; flex-shrink: 0; font-size: 9px; letter-spacing: 0.14em; color: var(--gray2); border: 1px solid rgba(11,11,9,0.18); border-radius: 2px; padding: 3px 7px; }
.vs-empty { grid-column: 1 / -1; padding: 60px 0; font-size: 13px; color: var(--gray2); }

/* footer */
.vs-footer { border-top: 3px solid var(--ink); display: flex; justify-content: space-between; padding: 22px 64px 60px; font-size: 10px; letter-spacing: 0.16em; color: var(--gray2); }
.vs-footer a { color: var(--gray2); text-decoration: none; }
.vs-footer a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ============================================================ MOBILE (≤ 800px, native scale) */
@media (max-width: 800px) {
  .vs-root { width: 100% !important; overflow-x: hidden; }
  .vs-pad { padding-left: 20px; padding-right: 20px; }
  .vs-header { height: 56px; border-bottom-width: 2px; }
  .vs-wordmark { gap: 8px; }
  .vs-wordmark .mk { font-size: 23px; }
  .vs-wordmark .sub { display: none; }
  .vs-nav { gap: 14px; }
  .vs-nav a.vs-cta { padding: 13px 16px; font-size: 11px; gap: 7px; min-height: 44px; box-sizing: border-box; }
  .vs-strip { font-size: 9px; letter-spacing: 0.14em; padding: 16px 0 18px; }
  .vs-display { font-size: 26vw; }
  .vs-lede { flex-direction: column; align-items: flex-start; gap: 16px; padding: 24px 0 20px; }
  .vs-lede p { font-size: 13.5px; line-height: 1.95; }
  .vs-toolbar { gap: 14px; padding: 15px 0; }
  .vs-toolbar input { font-size: 16px; }
  .vs-list { grid-template-columns: 1fr; margin-bottom: 64px; }
  .vs-row { padding: 16px 0; gap: 14px; }
  .vs-row .idx { width: 28px; }
  .vs-row .name { font-size: 16px; }
  .vs-row svg { opacity: 1; color: var(--gray2); }
  .vs-footer { flex-direction: column; gap: 8px; padding: 20px 20px 48px; line-height: 1.6; }
}
`;

function StoresPage() {
  const [stores, setStores] = React.useState(null); // null = loading
  const [error, setError] = React.useState(null);
  const [q, setQ] = React.useState('');

  React.useEffect(() => {
    fetchStores().then(setStores).catch((e) => setError(String(e)));
  }, []);

  // isVisible === false の店舗は一覧に出さない（未設定/true は表示）。件数の母数もこれ基準
  const visibleStores = React.useMemo(
    () => (stores ? stores.filter((s) => s.isVisible !== false) : []),
    [stores]
  );

  const filtered = React.useMemo(() => {
    const t = q.trim().toLowerCase();
    const matched = t ? visibleStores.filter((s) => s.name.toLowerCase().includes(t)) : visibleStores;
    // OPEN（isActive）を先、CLOSED を後に。各グループ内は
    // 英数(A–Z) → 記号 → 日本語(五十音) のブロック順、ブロック内は照合器で整列
    return matched.slice().sort((a, b) => {
      const group = (b.isActive ? 1 : 0) - (a.isActive ? 1 : 0);
      if (group !== 0) return group;
      const block = storeBlock(a) - storeBlock(b);
      if (block !== 0) return block;
      return STORE_COLLATOR.compare(storeSortKey(a), storeSortKey(b));
    });
  }, [visibleStores, q]);

  return (
    <div className="vs-root" data-screen-label="ストア一覧">
      <style>{VS_CSS}</style>

      <header className="vs-header vs-pad">
        <a className="vs-wordmark" href="index.html">
          <span className="mk cond">OGS</span>
          <span className="sub mono">OFFICIAL GOODS STORE — BY RENI</span>
        </a>
        <nav className="vs-nav mono">
          <a href="index.html">← TOP</a>
          <a className="vs-cta" href="index.html#vc-contact">お問い合わせ <OGSArrow></OGSArrow></a>
        </nav>
      </header>

      <section className="vs-pad">
        <div className="vs-strip mono">
          <span>WORKS — ALL STORES</span>
          <span>SINCE 2015</span>
        </div>
        <h1 className="vs-display cond">STORES</h1>
        <div className="vs-lede">
          <p>2015年の創業以降に運営してきたアーティスト・イベントのオフィシャルストア。ストア名を選ぶと、各ストアへ移動します。※過去の運営実績を含みます。</p>
          <div className="vs-count mono">
            {stores ? <span><b>{String(filtered.length).padStart(3, '0')}</b> / {String(visibleStores.length).padStart(3, '0')} STORES</span> : 'LOADING…'}
          </div>
        </div>

        <div className="vs-toolbar mono">
          <label htmlFor="vs-q">SEARCH</label>
          <input id="vs-q" type="text" value={q} placeholder="ストア名で絞り込む" onChange={(e) => setQ(e.target.value)}></input>
        </div>

        <div className="vs-list">
          {error && <div className="vs-empty mono">FAILED TO LOAD — {error}</div>}
          {!error && stores && filtered.length === 0 && <div className="vs-empty">「{q}」に一致するストアはありません。</div>}
          {!error && filtered.map((s, i) => (
            s.isActive ? (
              <a className="vs-row" key={s.id} href={s.url} target="_blank" rel="noopener noreferrer">
                <span className="idx mono">{String(i + 1).padStart(3, '0')}</span>
                <span className="name">{s.name}</span>
                <OGSArrow size={12}></OGSArrow>
              </a>
            ) : (
              <div className="vs-row vs-row--static" key={s.id}>
                <span className="idx mono">{String(i + 1).padStart(3, '0')}</span>
                <span className="name">{s.name}</span>
                <span className="vs-tag mono">CLOSED</span>
              </div>
            )
          ))}
        </div>
      </section>

      <footer className="vs-footer mono">
        <span>© RENI Co., Ltd.</span>
        <span><a href="privacy.html">プライバシーポリシー</a> / <a href="legal.html">特定商取引法に基づく表記</a></span>
        <span>OFFICIAL GOODS STORE — BY RENI CO., LTD.</span>
      </footer>
    </div>
  );
}

function StoresApp() {
  const [vw, setVw] = React.useState(window.innerWidth);
  React.useEffect(() => {
    const onResize = () => setVw(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  // ≤800px: native 1-column layout. Above: scale the 1280px design to fit.
  const zoom = vw <= 800 ? 1 : Math.min(vw / 1280, 1.5);
  return (
    <div style={{ zoom }}>
      <StoresPage></StoresPage>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<StoresApp></StoresApp>);
