/**
* 施設名 → 施設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);
}