/**
 * 確定期間KPI（`GET /reports/kpi`）の ★対象顧客をどう決めるか★（指示書AK ★段階6★ / §2-1）。
 *
 * ------------------------------------------------------------------------
 * ★★なぜ「1社に定まるときだけ出す」のか★★
 * ------------------------------------------------------------------------
 * 確定期間のKPIは `customer_summaries` を読む（D-102）。
 * ★★このサマリは顧客ごとの行で、複数顧客ぶんを合算する材料がDBに無い★★（指示書AK §2-1 の表）:
 *
 * | 指標 | 分子 | 分母 | 合算 |
 * |---|---|---|---|
 * | 清掃完了率 | ★1回ごとの完了率の合計が無い★ | ★件数が無い★ | ★★できない★★（`clean_area` / `task_area` の合計で割ると Σ面積 ÷ Σ面積 になり、サマリの式（1回ごとの完了率の平均。第10回 N-3）と違う。D-155） |
 * | 出発成功率 | `launch_count` | `task_count`（★P-6=A で確定★） | 材料はある |
 * | ★稼働率★ | ★列が無い（正常終了の回数）★ | `task_count` | ★★できない★★ |
 * | ★エラー発生率★ | `error_count` | ★`task_count`（P-6=B）★ | 材料はある |
 *
 * ★4指標を揃えて出せない（稼働率・清掃完了率の材料が無い）ので、合算しない方針は変えない★
 * （第11回 P-6 で出発成功率・エラー発生率の材料はそろったが、指示書AU §11 Q3 で方針は据え置き。D-158）。
 *
 * ★率の平均を取るのは誤り★（施設数の違いが消える）。だから ★合算せず、1社に定まるときだけ出す★。
 *
 * ------------------------------------------------------------------------
 * ★★決定表（指示書AK §2-1。覆すなら理由を残すこと）★★
 * ------------------------------------------------------------------------
 * | 入力 | 結果 |
 * |---|---|
 * | `customer_id` が指定されている | ★その顧客★（★存在確認はしない★。6-9） |
 * | `group_id` が指定されている・傘下1社 | ★その顧客★ |
 * | `group_id` が指定されている・傘下2社以上 | ★★422★★ |
 * | どちらも未指定・顧客が1社だけ | ★その顧客★（★P1 はここ★） |
 * | どちらも未指定・顧客が2社以上 | ★★422★★ |
 *
 * ★★「静かに1社を選ぶ」ことは絶対にしないこと★★（★落とし穴6★。D-133 が明確に否定している）—
 *   ★`customer_id` が明示されている場合を除き、候補が2つ以上あれば必ず 422★。
 *   ★どの顧客かを決める根拠が無いまま選ぶと、選び間違いに誰も気づけない★。
 *
 * ★★500 ではなく 422★★ — ★サーバの不具合ではなく「指定が足りない」という話★だから
 *   （指示書AK §2-1）。★候補を `details.customer_ids` で返す★ので、画面は
 *   「どれを選べばよいか」を文言を読まずに示せる。
 *
 * ★純粋関数にすること★（`CLAUDE.md` §9.4）— DBアクセスもログ出力も混ぜない
 *   （顧客一覧は呼び出し側が `db.customers()` で読んで渡す）。
 */
import type { MasterIdFilters } from "@/lib/api/master-id";
import type { Customer } from "@/lib/types";

/** 対象顧客が1社に定まった */
export interface TargetCustomerResolved {
  ok: true;
  customerId: number;
}

/**
 * 対象顧客が定まらなかった（候補が0社、または2社以上）。
 * ★`candidateCustomerIds` はそのまま 422 の `details.customer_ids` になる★。
 */
export interface TargetCustomerAmbiguous {
  ok: false;
  candidateCustomerIds: number[];
}

export type TargetCustomerResult = TargetCustomerResolved | TargetCustomerAmbiguous;

/**
 * 422 の文言（指示書AK §2-1 の原文。★勝手に言い換えないこと★）。
 * ★定数にしてあるのは、ルートとテストで同じ文字列を見るため★。
 */
export const TARGET_CUSTOMER_REQUIRED_MESSAGE =
  "顧客またはグループを指定してください。複数の顧客のデータを合算した数値は出せません。";

/**
 * 確定期間KPIの対象顧客を決める。
 *
 * @param customers ★顧客マスタの全行★（`db.customers()` の戻り値。`group_id` は
 *   ★API の形（`null` = ホールディングスなし）★に変換済み。D-011 / `groupIdFromDb()`）
 * @param filters `parseMasterIdFilters()` が検証済みの `groupId` / `customerId`
 *
 * ★★`customerId` が指定されていたら、顧客マスタを一切見ない★★ —
 *   ★存在確認をしない★（指示書AK 期待値表 6-9）。存在しないIDなら
 *   ★サマリが1行も当たらず 200・空★になるだけで、それは「該当なし」という正常な結果。
 *   ★404 にも 422 にもしないこと★（「データがまだ無い期間」と区別がつかなくなる。
 *   `app/api/reports/kpi/route.ts` の `emptyKpi()` と同じ考え方）。
 *   ★`groupId` と食い違っていても `customerId` を優先する★ — 決定表の1行目がそう定めている。
 *
 * ★★傘下0社（`groupId` 指定で候補が1社も無い）も 422★★（★指示書に明記が無い箇所★。
 *   起草者の判断。理由は3つ）:
 *   1. ★この関数の契約は「1社に定まるときだけ ok」★で、0社は定まっていない
 *   2. ★200・空で返すと「対象顧客なし」を静かに選んだことになる★ — 候補0社の 422 は
 *      ★画面に「その条件では顧客が選べない」と伝えられる★（`details.customer_ids` が空配列）
 *   3. ★6-9（存在しない `customer_id` → 200・空）とは事情が違う★ — あちらは
 *      ★顧客を1社に決めたうえでサマリが0行★だが、こちらは ★決める相手がいない★
 */
export function resolveTargetCustomer(
  customers: readonly Pick<Customer, "id" | "group_id">[],
  filters: MasterIdFilters
): TargetCustomerResult {
  // ★★`!== null` で分岐すること★★ — `filters.customerId || ...` と書くと、
  //   将来 0 が入り込んだときに「未指定」に化ける（`lib/facilities/resolve-by-master.ts` と同じ作法）
  if (filters.customerId !== null) {
    return { ok: true, customerId: filters.customerId };
  }

  const candidates =
    filters.groupId === null
      ? customers
      : customers.filter((customer) => customer.group_id === filters.groupId);

  if (candidates.length === 1) {
    return { ok: true, customerId: candidates[0].id };
  }
  // ★0社も2社以上もここ★（★1社のときだけが ok★）
  return { ok: false, candidateCustomerIds: candidates.map((customer) => customer.id) };
}
