/**
 * 施設IDの絞り込み結果どうしの積集合（指示書AK 段階3 §6-2）。
 *
 * ★★「施設名の絞り込み」と「分類の絞り込み」は同時に指定されうる★★ので、
 * ★両方の結果を AND で畳んでから `where: { facility_id: { in: [...] } }` に渡します★
 * （★どちらか片方を捨てないこと★。指示書AK §2-2）。
 *
 * ★★`null` と `[]` の意味は `resolveFacilityIdsByName()` / `resolveFacilityIdsByMasters()`
 *   と同じ★★:
 *   - `null` … ★その条件は指定されていない（絞らない）★
 *   - `[]`   … ★指定されたが0件ヒット★
 *
 * 期待値表（★この6行がこの関数の契約★。テストで固定してある）:
 *
 * | 施設名の解決結果 | 分類の解決結果 | 戻り値 |
 * |---|---|---|
 * | `null`    | `null`    | ★`null`★ |
 * | `[1,2,3]` | `null`    | `[1,2,3]` |
 * | `null`    | `[2,3,4]` | `[2,3,4]` |
 * | `[1,2,3]` | `[2,3,4]` | ★`[2,3]`★ |
 * | ★`[1]`★  | ★`[2]`★  | ★★`[]`★★（★`null` にしないこと★） |
 * | `[]`      | `[1,2]`   | ★`[]`★ |
 *
 * ★★最後の2行が要です★★（★落とし穴2★）— ★積集合が空になったら `[]` を返すこと★。
 *   ★`null` にすると条件ごと消えて全件表示に化けます。例外もテスト失敗も出ません。★
 *
 * ★純粋関数にすること★（`CLAUDE.md` §9.4）— DBアクセスやログ出力を混ぜない。
 */

/**
 * 2つの施設ID絞り込み結果の積集合を取る。
 *
 * ★★`a ?? b` や `[...(a ?? []), ...(b ?? [])]` で済ませないこと★★ —
 *   前者は片方を捨て、後者は和集合（OR）になります。★契約は AND★。
 *
 * ★並び順は `a` の順序を保つ★（★並び替えない★）—
 *   `facility_id: { in: [...] }` は順序を見ないので意味は変わりませんが、
 *   ★入力の順序を勝手に変えるとテストの期待値が理由なく揺れます★。
 *
 * ★重複は取り除かない★ — 解決側（`facilityIdsMatchingName()` /
 *   `facilityIdsMatchingMasters()`）が施設マスタを1行ずつ写した配列を返すので、
 *   ★そもそも重複が入らない★。ここで `Set` に通して「直した」ことにすると、
 *   ★重複が出る不具合が上流で起きても気づけなくなります★。
 */
export function intersectFacilityIds(
  a: number[] | null,
  b: number[] | null
): number[] | null {
  // ★★`!== null` で分岐すること★★ — `a?.length` で書くと `[]`（0件ヒット）が
  //   「未指定」に化け、★0件が全件表示になります★（罠1 / 落とし穴1・2）
  if (a === null) return b;
  if (b === null) return a;
  const inB = new Set(b);
  return a.filter((id) => inB.has(id));
}
