/**
 * 施設名 → 施設ID の解決（指示書AC 段階2 ★D-6★ / ★C-1★）。
 *
 * ------------------------------------------------------------------------
 * ★★なぜ「名前で絞る」のではなく「IDで絞る」のか★★（指示書AG の帰結。2026-09-13）
 * ------------------------------------------------------------------------
 * 施設名の検索は ★記号（中点・長音・空白・ハイフン類）を無視した部分一致★ です
 * （`lib/format/search-text.ts` の `includesFoldName`。指示書AG D-1〜D-5）。
 * ★`ドンキホーテ` と打って `ドン・キホーテ戸塚` が引けます★。
 *
 * ★★この照合を MariaDB 側で再現する手段がありません★★:
 *   - 中点を無視して比較する関数が MariaDB に無い
 *   - 用意するには生成カラム＋索引＝★スキーマ変更（`CLAUDE.md` §7 で禁止）★
 *   - 生SQL も禁止（指示書AC §E-1。D-122 の ESLint ルールが落とす）
 *
 * > ★★★「MySQL の ci（case-insensitive）照合順序なら中点も無視されるはず」は誤り。実測済み★★★
 * > `'ドン・キホーテ戸塚' LIKE '%ドンキホーテ%'` は
 * > ★`utf8mb4_unicode_ci` / `utf8mb4_general_ci` / `utf8mb4_0900_ai_ci` の**3種すべてで 0**★
 * > （中点入りの `'%ドン・キホーテ%'` なら 1）。
 * > ★★この段落を消さないこと★★ — 後任が「照合順序でできるはずだ」と戻ってくるのを
 * > 防ぐための実測記録です（指示書AG 段階5 / 罠5）。
 *
 * ★★だから照合はアプリ側（施設マスタ12件）で行い、当たった**施設IDの配列**を
 *   `where: { facility_id: { in: [...] } }` に渡します★★。
 *   ★清掃ログ本体（本番想定で約26万行）は SQL で絞ったまま★なので、
 *   ★むしろ日付だけで絞るより縮みます★。
 *
 * > ★★Prisma の `contains` を使う案に戻らないこと★★ — ★`contains` は `%` と `_` を
 * > エスケープしません★（実測: `contains: "%"` → 施設12件が全部ヒット。
 * > JS の `.includes("%")` なら0件）。★本案なら施設名を SQL に渡さないので、
 * > この問題自体が発生しません★。
 *
 * ★★照合規則をここに書き下さないこと★★ — 正は `includesFoldName` の1本です
 * （`CLAUDE.md` §4.6 が契約フラグに課したのと同じ規律）。
 */
import { db } from "@/lib/db";
import { includesFoldName } from "@/lib/format/search-text";
import type { FacilityNameRef } from "@/lib/types";

/**
 * 施設名の条件を施設IDの配列に解決する★純粋関数★（DBアクセスを混ぜない）。
 *
 * @returns
 *   - ★★`null`★★ … 施設名の条件が無い（★空文字★）。＝ ★施設で絞らない★
 *   - ★★`[]`★★   … 照合したが ★1件も当たらなかった★。＝ ★結果は0件★
 *   - それ以外 … 当たった施設IDの配列
 *
 * ★★空文字で `[]` を返さないこと★★ — `[]` は「0件ヒット」の意味なので、
 *   ★施設名を入力していないのに一覧が空になります★（`CleanLogDbQuery.facility_ids`
 *   の型コメント / 罠1）。
 *
 * ★`ドンキ` のように複数施設に当たりうる★ので戻り値は配列です
 * （`facility_id`（単数）にしないこと）。
 */
export function facilityIdsMatchingName(
  refs: readonly FacilityNameRef[],
  name: string
): number[] | null {
  if (name === "") return null;
  return refs
    .filter((ref) => includesFoldName(ref.facility_name, name))
    .map((ref) => ref.id);
}

/**
 * 施設名の条件を施設IDの配列に解決する（★API ルートから呼ぶ★）。
 *
 * ★★施設ごとにループでクエリを撃たないこと★★ — ★1クエリで全件引いてメモリで照合する★
 *   （施設マスタは実測12件。指示書AC D-6）。
 *
 * ★★空文字のときは DB を読まずに `null` を返す★★ — 読んでも結果が変わらないので、
 *   ★「施設名を入れていない検索」で余計な往復を増やさない★。
 *
 * ★★解決用の読み取りは `deleted_at` で絞らない★★（★罠7★）—
 *   理由は `loadFacilityNameIndex()`（`lib/db/facility-name-source.ts`）の docstring。
 *   ★`db.facilities()` を流用しないこと★。
 */
export async function resolveFacilityIdsByName(name: string): Promise<number[] | null> {
  if (name === "") return null;
  return facilityIdsMatchingName(await db.facilityNameIndex(), name);
}
