import { describe, expect, it } from "vitest";

import { DB_IN_CLAUSE_MAX_ITEMS } from "@/lib/config";
import { chunkForInClause } from "@/lib/db/chunked";

/**
 * 指示書AC 段階2 ★§E-5★ — `in` に入れる配列の長さに上限を設ける。
 *
 * ★上限値と、その値にした理由は `DB_IN_CLAUSE_MAX_ITEMS`（`lib/config/index.ts`）★。
 * ★ここに数字を直書きしないこと★（`CLAUDE.md` §7）。
 */
describe("chunkForInClause（指示書AC §E-5）", () => {
  it("★空配列は「塊が0個」★（★`[[]]` を返さない＝ `in: []` のクエリを撃たない★）", () => {
    // ★`[[]]` を返すと、必ず0件と分かっているクエリが1本走って往復が無駄になる★
    expect(chunkForInClause([])).toEqual([]);
  });

  it("上限以下なら1つの塊のまま", () => {
    const ids = Array.from({ length: DB_IN_CLAUSE_MAX_ITEMS }, (_, i) => i);
    expect(chunkForInClause(ids)).toEqual([ids]);
  });

  it("上限を1つ超えたら2つに割れる", () => {
    const ids = Array.from({ length: DB_IN_CLAUSE_MAX_ITEMS + 1 }, (_, i) => i);
    const chunks = chunkForInClause(ids);
    expect(chunks).toHaveLength(2);
    expect(chunks[0]).toHaveLength(DB_IN_CLAUSE_MAX_ITEMS);
    expect(chunks[1]).toHaveLength(1);
  });

  it("★分割しても要素は1つも落ちず、順序も変わらない★", () => {
    const ids = Array.from({ length: 2_345 }, (_, i) => i);
    expect(chunkForInClause(ids).flat()).toEqual(ids);
  });

  it("本番想定（31日 × 約360行/日 ≒ 11,000件）でもクエリ回数が有界", () => {
    const ids = Array.from({ length: 11_000 }, (_, i) => i);
    const chunks = chunkForInClause(ids);
    expect(chunks).toHaveLength(Math.ceil(11_000 / DB_IN_CLAUSE_MAX_ITEMS));
    // ★Prisma（mysql）の `maxBindValues` 65,535 を1本のクエリで超えない★
    expect(Math.max(...chunks.map((c) => c.length))).toBeLessThan(65_535);
  });

  it("分割サイズが0以下なら例外（★無限ループに落ちるより気づけるほうを選ぶ★）", () => {
    expect(() => chunkForInClause([1, 2, 3], 0)).toThrow();
    expect(() => chunkForInClause([1, 2, 3], -1)).toThrow();
  });

  it("bigint の配列でも使える（★`clean_timetables.id` は BigInt★）", () => {
    expect(chunkForInClause([BigInt(1), BigInt(2), BigInt(3)], 2)).toEqual([[BigInt(1), BigInt(2)], [BigInt(3)]]);
  });
});
