import { describe, expect, it } from "vitest";

import {
  loadFacilityGroupIndex,
  type FacilityGroupRow,
  type FacilityGroupSourcePrisma,
} from "@/lib/db/facility-group-source";

/**
 * 指示書AK 段階3 ★§6-3 / 罠7 / 落とし穴3・14★ —
 * 分類（ホールディングス／屋号）の解決用の読み取り。
 *
 * ★★`deleted_at` で絞らない★★: `cleanLogs()` は論理削除された施設のログも返している
 *   （`include: { facility: true }` に条件が無い）。ここで絞ると
 *   ★屋号で絞った瞬間にそのログが静かに消える★（★件数が減るだけで例外も出ない★）。
 *   ★`loadFacilityNameIndex()` と扱いを揃えてある★。
 *
 * ★★ローカルDBでは検出できません★★ — 実測で `facilities` 12件のうち論理削除は0件。
 *   ★だから合成データで固定する★（`facility-name-source.test.ts` と同じ理由）。
 */

/** `findMany()` に渡された引数を控えるスタブ（★DBに接続しない★） */
function stubPrisma(rows: readonly FacilityGroupRow[]): {
  prisma: FacilityGroupSourcePrisma;
  calls: unknown[];
} {
  const calls: unknown[] = [];
  const prisma: FacilityGroupSourcePrisma = {
    facility: {
      async findMany(args) {
        calls.push(args);
        return [...rows];
      },
    },
  };
  return { prisma, calls };
}

function row(id: number, customerId: number, groupId: number): FacilityGroupRow {
  return {
    id: BigInt(id),
    customer_id: BigInt(customerId),
    customer: { group_id: BigInt(groupId) },
  };
}

describe("loadFacilityGroupIndex（指示書AK 段階3 §6-3）", () => {
  it("★★`where` を一切付けない★★（＝ `deleted_at` で絞らない。罠7 の唯一の検出手段）", async () => {
    const { prisma, calls } = stubPrisma([]);
    await loadFacilityGroupIndex(prisma);

    for (const args of calls) {
      expect(Object.keys(args as object)).toEqual(["select"]);
    }
  });

  it("★★クエリは1本だけ★★（施設ごとにループで撃たないこと。落とし穴14）", async () => {
    const { prisma, calls } = stubPrisma([row(1, 1, 1), row(2, 2, 1), row(3, 3, 0)]);
    await loadFacilityGroupIndex(prisma);
    expect(calls).toHaveLength(1);
  });

  it("読む列は `id` / `customer_id` / 屋号の `group_id` だけ（★これ以上増やさない★）", async () => {
    const { prisma, calls } = stubPrisma([]);
    await loadFacilityGroupIndex(prisma);
    const select = (calls[0] as { select: Record<string, unknown> }).select;
    expect(Object.keys(select).sort()).toEqual(["customer", "customer_id", "id"]);
    expect(select.customer).toEqual({ select: { group_id: true } });
  });

  it("★論理削除された施設も返す★（返す行を間引かないこと）", async () => {
    const { prisma } = stubPrisma([row(1, 1, 1), row(2, 1, 1)]);
    const rows = await loadFacilityGroupIndex(prisma);
    expect(rows.map((r) => r.id)).toEqual([1, 2]);
  });

  it("★BigInt を `number` に直して返す★（`NextResponse.json` が BigInt で例外を投げるため）", async () => {
    const { prisma } = stubPrisma([row(12, 4, 2)]);
    const rows = await loadFacilityGroupIndex(prisma);
    expect(rows).toEqual([{ id: 12, customer_id: 4, group_id: 2 }]);
    expect(typeof rows[0].id).toBe("number");
    expect(typeof rows[0].customer_id).toBe("number");
  });

  it("★★`customers.group_id = 0` は `null` に変換する★★（D-011 / 落とし穴3）", async () => {
    // ★0 は「ホールディングスなし」のセンチネル★ — そのまま出すと
    //   「グループ0」という存在しないホールディングスに当たってしまう
    const { prisma } = stubPrisma([row(5, 4, 0)]);
    const rows = await loadFacilityGroupIndex(prisma);
    expect(rows[0].group_id).toBeNull();
  });
});
