import type { AreaCompletionRow } from "@/lib/types";
import { cleanLogs, type FixtureCleanLogItem } from "./clean-logs";
import { dateOnly } from "./_helpers";

/**
 * エリア別完了率（GET /clean-logs/{id}/areas）のサンプルデータ
 *
 * docs/fixtures.md §2.5 のケースを網羅しています。
 *
 * ★★1エリア = 1清掃ロボット★★（`CLAUDE.md` §8 / TeamWorks 第8回 A9-2）。
 *   ★1回の清掃の中を売り場・通路・レジ前に区切った内訳ではありません★（その粒度は取得不可）。
 *
 * ★★日付と id は `cleanLogs` の**既存の行から引いています**★★ —
 *   手書きの日付を新たに作ると、`lib/fixtures/clean-logs.ts` が実行日相対で日付を作っている
 *   ため、日をまたいだ瞬間に「同じ施設・同じ日」の突き合わせが外れます。
 *   ★`lib/fixtures/` の既存ファイルは1行も変更していません（追加のみ）★。
 *
 * ★行の `completion_rate` は対応する `CleanLogItem.completion_rate` と同じ値にしてあります★ —
 *   一覧（清掃詳細ログ）の行と、そこから開くモーダルの行で数字が食い違わないようにするため
 *   （指示書Z 決定6 と同じ理由）。
 *   ★★行の完了率は面積から `runCompletionRate()` で決まる★★（D-155）— ★面積は `completion_rate` から作り、
 *   `runCompletionRate()` で元の完了率に戻ることをテストで確かめている★（`__tests__/areas.test.ts`）。
 *   ★★全体率（`overall_completion_rate`）は行の完了率の単純平均★★
 *   （清掃1回＝1票。未実施の行は分母に入れない。`lib/judge/kpi.ts` の `meanCleanRatio()`）。
 *   ★指示書AQ §5-5 で面積ベース（Σclean_area ÷ Σtask_area）から変わった★（旧: 指示書Z 決定7）。
 *   ★期間の合計どうし（Σclean_area ÷ Σtask_area）は全体率の分子・分母ではない★。
 */

/**
 * ★シリアル → `section_id`★（末尾4桁を数値にする）
 *
 * ローカルDBのシードは ★`section_id` をロボットの id と同じにしている★
 * （`scripts/seed-local-db.ts`。1清掃エリア=1ロボット）。fixtures でも
 * 「エリアIDはロボットに対応する」という関係が見えるように、シリアルから決定的に作る。
 */
function sectionIdOf(serialNumber: string): number {
  return Number(serialNumber.slice(-4));
}

/**
 * ★`section_name` はシードと同じ形★（`清掃エリア（ロボット107）`）。
 *   ★モックの「1F 生鮮エリア」のような名前は作らない★ — 実装では
 *   `sections.section_name` の生値をそのまま出すため、fixtures も同じ流儀に揃える
 *   （実在の店舗名・エリア名を入れないこと。docs/fixtures.md §1）。
 */
function sectionNameOf(sectionId: number): string {
  return `清掃エリア（ロボット${sectionId}）`;
}

interface AreaGroupSpec {
  facility_id: number;
  /** `cleanLogs` と同じ「n日前」。日付は `dateOnly()` で引く */
  daysBack: number;
  /** 何を確認するためのケースか（docs/fixtures.md §2.5 と対応させる） */
  note: string;
  /**
   * シリアル → `task_area`（清掃対象面積）。
   * ★キーの集合が「その施設・その日のエリアの集合」そのもの★なので、
   * `cleanLogs` 側の行と数が合わなければ組み立て時に落ちる（fail-loud）。
   * ★未実施・履歴なしの行は `0`★（指示書Z 決定13 / 3-2）。
   */
  taskAreaBySerial: Record<string, number>;
}

/**
 * ★★仕込んだケース（docs/fixtures.md §2.5 と同じ表）★★
 *
 * 完了率は `cleanLogs` 側が持っている値なので、ここでは面積だけを決める。
 * ★`clean_area = task_area × completion_rate ÷ 100`★ になるように組み立てる（小数第6位で丸める）。
 * ★行の完了率は面積から `runCompletionRate()` で決まる★（D-155）。★全体率は行の完了率の単純平均★
 * （Σ面積 ÷ Σ面積 ではない。指示書AQ §5-5）。
 * 施設7 の担当面積を違えてあるのは、★面積ベースに戻ったら判定が変わって気づける★ようにするため。
 */
const AREA_GROUPS: AreaGroupSpec[] = [
  // --- 全体率の境界（1エリアだけの施設。行の完了率がそのまま全体率になる） ---
  {
    facility_id: 1,
    daysBack: 0,
    note: "1エリアのみ / 全体 90.1% = ◎（90%以上）",
    taskAreaBySerial: { "8111R5628060011": 1000 },
  },
  {
    facility_id: 2,
    daysBack: 0,
    note: "1エリアのみ / 全体 ちょうど90.0% = ◎（90%以上が◎。★D-139 で ○ から変わった★）",
    taskAreaBySerial: { "8111R5628060022": 1000 },
  },
  {
    facility_id: 5,
    daysBack: 0,
    note: "1エリアのみ / 全体 ちょうど70.0% = ○（70%以上が○。★D-139 で △ から変わった★）",
    taskAreaBySerial: { "8111R5628060055": 1000 },
  },
  {
    facility_id: 8,
    daysBack: 0,
    note: "1エリアのみ / 全体 ちょうど10.0% = △（10%未満が×）",
    taskAreaBySerial: { "8111R5628060122": 1000 },
  },
  {
    facility_id: 10,
    daysBack: 0,
    note: "1エリアのみ / 全体 0% = ×（★未実施ではなく「0%で実施した」★）",
    taskAreaBySerial: { "8111R5628060144": 1000 },
  },
  {
    facility_id: 12,
    daysBack: 0,
    note: "★全エリア未実施★ / completion_rate は null、平均する行が0件なので全体率も null（0% にしない）",
    taskAreaBySerial: { "8111R5628060166": 0 },
  },

  // --- 複数エリアの施設 ---
  {
    facility_id: 7,
    daysBack: 1,
    note:
      "★3エリア★ かつ ★担当面積が違う★: 全体率は行の完了率の単純平均 (62+94+88)/3 = 81.33% (○)。" +
      "旧定義の面積ベース 802/1200 = 66.83…% (△) とは判定が割れる（面積ベースに戻っていないことの確認）",
    taskAreaBySerial: {
      "8111R5628060099": 1000, // 62.0%
      "8111R5628060100": 100, // 94.0%
      "8111R5628060177": 100, // 88.0%
    },
  },
  {
    facility_id: 5,
    daysBack: 1,
    note:
      "★★履歴の行が無いエリア（出発失敗）と正常終了のエリアが同じ施設・同じ日に混在する★★: " +
      "履歴なし側は completion_rate が null で単純平均の分母に入らないため、全体 97.0% = ◎",
    taskAreaBySerial: {
      "8111R5628060055": 0, // ★履歴の行が無い（出発失敗）→ 面積は 0★
      "8111R5628060088": 1000, // 97.0%
    },
  },

  // --- filter=problem で該当0件になる施設×日 ---
  {
    facility_id: 1,
    daysBack: 1,
    note: "★全エリアが◎○★ — filter=problem のとき items は空配列（404 にしない）",
    taskAreaBySerial: { "8111R5628060011": 1000 },
  },
];

/**
 * 面積は小数第6位まで（`clean_task_reports.task_area` / `clean_area` は `double(12,6)`。D-094）。
 * ★小数第1位（旧 `round1`）にしないこと★ — `task_area = 120` のような値で `runCompletionRate()` が
 *   元の完了率から1段ずれる（2026-10-02 に node で確認。指示書AS 6-2）。
 */
function round6(value: number): number {
  return Math.round(value * 1_000_000) / 1_000_000;
}

function logsOfGroup(group: AreaGroupSpec, date: string): FixtureCleanLogItem[] {
  return cleanLogs
    .filter(
      (log) =>
        log.facility_id === group.facility_id &&
        log.scheduled_at.slice(0, 10) === date
    )
    .sort((a, b) => a.id - b.id);
}

/**
 * ★組み立てに失敗したら落とす（fail-loud）★
 *
 * `lib/fixtures/clean-logs.ts` の行を書き換えると、ここの前提（施設×日のエリアの顔ぶれ）が
 * 黙ってズレる。黙ってズレると ★「そのエリアが存在しない日」ができて画面が空になる★ ので、
 * 読み込み時に気づけるようにしてある。
 */
function build(): AreaCompletionRow[] {
  const out: AreaCompletionRow[] = [];
  for (const group of AREA_GROUPS) {
    const date = dateOnly(group.daysBack);
    const logs = logsOfGroup(group, date);
    const serials = Object.keys(group.taskAreaBySerial);
    if (logs.length !== serials.length) {
      throw new Error(
        `[fixtures/areas] 施設${group.facility_id} の ${date} は clean-logs に ${logs.length}件ありますが、` +
          `AREA_GROUPS は ${serials.length}件（${serials.join(" / ")}）を想定しています`
      );
    }
    for (const log of logs) {
      const taskArea = group.taskAreaBySerial[log.serial_number];
      if (taskArea === undefined) {
        throw new Error(
          `[fixtures/areas] 施設${group.facility_id} の ${date} に想定外のシリアル ${log.serial_number} があります`
        );
      }
      const rate = log.completion_rate;
      if (rate === null && taskArea !== 0) {
        throw new Error(
          `[fixtures/areas] 未実施（completion_rate=null）の行の task_area は 0 にしてください: ${log.serial_number}`
        );
      }
      const sectionId = sectionIdOf(log.serial_number);
      out.push({
        clean_log_id: log.id,
        facility_id: log.facility_id,
        date,
        section_id: sectionId,
        section_name: sectionNameOf(sectionId),
        completion_rate: rate,
        // ★未実施は 0%（clean_area=0）ではなく「面積を持たない」★ — 分母に載せない
        task_area: taskArea,
        clean_area: rate === null ? 0 : round6((taskArea * rate) / 100),
        // ★結果待ちは清掃ログの値を写す（データの値を新しく作らない。指示書BB A3）★
        result_pending: log.result_pending,
      });
    }
  }
  return out;
}

export const areaCompletions: AreaCompletionRow[] = build();
