import { PRECHECK_MINUTES_BEFORE } from "@/lib/config";
import type { ErrorReportRowSource, ErrorTimetableRef } from "@/lib/db/error-report-mapping";
import { toDbJst } from "@/lib/format/date";
import { at } from "./_helpers";
import { dsDevices } from "./ds-devices";
import { facilities } from "./facilities";

/**
 * エラー一覧（`/errors`。指示書AW 段階3-2）のサンプルデータ。docs/fixtures.md §13。
 *
 * ★DB の行の形で持つ★（指示書AW D13）— 日時は `toDbJst()` を通した壁時計値の `Date`、id は `bigint`。
 *   ★fixtures と Prisma は同じ純粋関数★（`errorReportKeyFromRow()` / `filterSortErrorReportKeys()` /
 *   `errorReportItemFromRow()`）を通すので、`USE_FIXTURES` で件数や並びが変わらない（`CLAUDE.md` §9.5）。
 *
 * ★既定（未解消・対応中）で10行（`FX-ERR-03` 以外）・「解消済みも表示」で11行★。
 *
 * ★★値はダミー★★:
 *   - 実在の店名・住所を入れない（`CLAUDE.md` §7）。★施設名は `facilities` から id で引く★（直書きしない）
 *   - ★`error_level` の値（`CRITICAL` 以外）と、`SECURITY_ON` / `D-S_CONTROL_FAILURE` の `error_level` は
 *     実データ未受領のダミー★。`NOT_STARTED`・`CRITICAL`・`serial_number` `'0'` の組はステージングの実測の形
 *     （D-158 2026-10-06 実測・先方投入）
 *   - `error_type` は生値のまま画面に出る（区分を作らない。D-077 / D-160 項3）
 *
 * ★`robot_id` は読まない★（`select` で絞る。指示書AU 段階0-4）。fixtures でも値は使わない —
 *   ★制御盤・セキュリティ・清掃開始失敗の行は `robot_id` が NULL★ という実データの形を示すためだけに持つ。
 *
 * ★予定（`errorReportTimetables`）は本表・履歴を分けない★（fixtures には2表の区別が無い。
 *   2表から引く規則は Prisma 版の `findTimetablesByChunks()` / `findTimetableById()`）。
 *
 * ★id は `"FX-ERR-01"` 〜★（`lib/fixtures/prechecks.ts` の id と重ならない。上書き表も別。指示書AW §11 Q8）。
 */

/** fixtures の行（★`robot_id` は読まない。実データの形を示すためだけ★） */
export interface FixtureErrorReportRow extends ErrorReportRowSource {
  robot_id: bigint | null;
}

// ★分数を直書きしないための別名（禁止事項3）★ — 30分前は設定値から読む
const SECOND_ROUND_MINUTES = PRECHECK_MINUTES_BEFORE[1];
// ★10 は設定（60/30）に無い値であることが本題なので直書きする★（`lib/fixtures/prechecks.ts` の `20` と同じ扱い。
//   D-105 項1: 設定に無い分数でも生の分数で「事前チェック（10分前）」と出す）
const UNCONFIGURED_MINUTES = 10;

/** 予定の id（`clean_timetables.id` 相当） */
const TT_MAIN_A = BigInt(9001);
const TT_PRECHECK_30 = BigInt(9002);
const TT_MAIN_B = BigInt(9003);
/** ★実DBでは `clean_timetable_histories` にだけある想定★（表1 E6。fixtures では2表を分けない） */
const TT_MAIN_HISTORY_ONLY = BigInt(9006);
const TT_PRECHECK_10 = BigInt(9009);
/** ★どの予定にも無い id★（表1 E5「予定不明」） */
const TT_MISSING = BigInt(9999);

/** 予定（本表・履歴をまとめたもの。`{ id, precheck }` だけ） */
export const errorReportTimetables: ErrorTimetableRef[] = [
  { id: TT_MAIN_A, precheck: 0 },
  { id: TT_PRECHECK_30, precheck: SECOND_ROUND_MINUTES },
  { id: TT_MAIN_B, precheck: 0 },
  { id: TT_MAIN_HISTORY_ONLY, precheck: 0 }, // 実DBでは履歴にだけある想定（E6）
  { id: TT_PRECHECK_10, precheck: UNCONFIGURED_MINUTES },
];

/** ★施設名は `facilities` から id で引く★（無ければ fixtures の誤りなので例外） */
function facilityOf(id: number): { facility_id: bigint; facility: { facility_name: string } } {
  const found = facilities.find((f) => f.id === id);
  if (!found) throw new Error(`[fixtures/error-reports] facilities に id=${id} がありません`);
  return { facility_id: BigInt(id), facility: { facility_name: found.facility_name } };
}

/** その施設の先頭のロボットのシリアル（直書きしない） */
function robotSerialOf(facilityId: number): string {
  const serial = facilities.find((f) => f.id === facilityId)?.robots[0]?.serial_number;
  if (!serial) throw new Error(`[fixtures/error-reports] 施設 id=${facilityId} にロボットがありません`);
  return serial;
}

/** その施設の D-S制御盤のシリアル（`lib/fixtures/ds-devices.ts` の実在の値） */
function controlPanelSerialOf(facilityId: number): string {
  const serial = dsDevices.find((d) => d.facility_id === facilityId)?.serial_number;
  if (!serial) throw new Error(`[fixtures/error-reports] 施設 id=${facilityId} に D-S制御盤がありません`);
  return serial;
}

/** n日前の HH:MM（JST）→ DB の壁時計値の `Date`（Prisma が返すのと同じ形） */
function dbAt(n: number, time: string): Date {
  return toDbJst(new Date(at(n, time)));
}

/** ロボットのエラー行の既定値（施設1・本清掃 A・未解消） */
function robotError(
  errorId: string,
  overrides: Partial<FixtureErrorReportRow> = {}
): FixtureErrorReportRow {
  return {
    error_id: errorId,
    robot_id: BigInt(101),
    clean_timetable_id: TT_MAIN_A,
    serial_number: robotSerialOf(1),
    ...facilityOf(1),
    error_type: "EmergencyStop",
    error_level: "ERROR",
    errored_at: dbAt(1, "23:10"),
    created_at: dbAt(1, "23:11"),
    status: 0,
    responded_at: null,
    resolved_at: null,
    comment: null,
    ...overrides,
  };
}

export const errorReportRows: FixtureErrorReportRow[] = [
  // E1: ロボットのエラー・本清掃に紐づく（未解消）
  robotError("FX-ERR-01"),
  // E2: ロボットのエラー・事前チェック（30分前）に紐づく（対応中・responded_at あり）
  robotError("FX-ERR-02", {
    clean_timetable_id: TT_PRECHECK_30,
    error_type: "LowBattery",
    error_level: "WARNING",
    errored_at: dbAt(1, "22:30"),
    created_at: dbAt(1, "22:31"),
    status: 1,
    responded_at: dbAt(0, "08:30"),
  }),
  // E3: 解消済み・本清掃（responded_at / resolved_at / 備考あり）— ★既定では出ない★
  robotError("FX-ERR-03", {
    robot_id: BigInt(103),
    serial_number: robotSerialOf(3),
    ...facilityOf(3),
    clean_timetable_id: TT_MAIN_B,
    error_type: "HandleUp",
    errored_at: dbAt(2, "23:40"),
    created_at: dbAt(2, "23:41"),
    status: 2,
    responded_at: dbAt(1, "08:20"),
    resolved_at: dbAt(1, "09:05"),
    comment: "現場でハンドルを戻してもらった",
  }),
  // E4: clean_timetable_id NULL（予定なし）
  robotError("FX-ERR-04", {
    robot_id: BigInt(102),
    serial_number: robotSerialOf(2),
    ...facilityOf(2),
    clean_timetable_id: null,
    error_type: "LostLocation",
    errored_at: dbAt(1, "15:20"),
    created_at: dbAt(1, "15:21"),
  }),
  // E5: clean_timetable_id がどの予定にも無い id（予定不明）
  robotError("FX-ERR-05", {
    robot_id: BigInt(102),
    serial_number: robotSerialOf(2),
    ...facilityOf(2),
    clean_timetable_id: TT_MISSING,
    error_type: "BrushError",
    errored_at: dbAt(1, "23:50"),
    created_at: dbAt(1, "23:51"),
  }),
  // E6: 予定が履歴にだけある想定・本清掃
  robotError("FX-ERR-06", {
    robot_id: BigInt(104),
    serial_number: robotSerialOf(4),
    ...facilityOf(4),
    clean_timetable_id: TT_MAIN_HISTORY_ONLY,
    error_type: "Offline",
    errored_at: dbAt(3, "23:05"),
    created_at: dbAt(3, "23:06"),
  }),
  // E7: errored_at NULL（検知時刻は created_at）・本清掃
  robotError("FX-ERR-07", {
    robot_id: BigInt(104),
    serial_number: robotSerialOf(4),
    ...facilityOf(4),
    clean_timetable_id: TT_MAIN_B,
    error_type: "Offline",
    errored_at: null,
    created_at: dbAt(0, "06:40"),
  }),
  // E8: ★robot_id NULL・serial_number '0'・error_type SECURITY_ON★（セキュリティのエラー。D-158 P-8）
  robotError("FX-ERR-08", {
    robot_id: null,
    serial_number: "0",
    ...facilityOf(5),
    clean_timetable_id: TT_MAIN_B,
    error_type: "SECURITY_ON",
    error_level: "ERROR", // ★ダミー（実データ未受領）★
    errored_at: dbAt(1, "22:55"),
    created_at: dbAt(1, "22:56"),
  }),
  // E9: 事前チェック（★10分前。設定に無い値★）
  robotError("FX-ERR-09", {
    clean_timetable_id: TT_PRECHECK_10,
    error_type: "LowBattery",
    error_level: "WARNING",
    errored_at: dbAt(1, "22:50"),
    created_at: dbAt(1, "22:51"),
  }),
  // P-8 a: ★robot_id NULL・serial_number = 制御盤の SN・error_type D-S_CONTROL_FAILURE★（制御盤のエラー）
  robotError("FX-ERR-10", {
    robot_id: null,
    serial_number: controlPanelSerialOf(1),
    clean_timetable_id: TT_MAIN_A,
    error_type: "D-S_CONTROL_FAILURE",
    error_level: "ERROR", // ★ダミー（実データ未受領）★
    errored_at: dbAt(1, "22:45"),
    created_at: dbAt(1, "22:46"),
  }),
  // ★robot_id NULL・serial_number '0'・error_type NOT_STARTED・error_level CRITICAL★
  //   （清掃を開始できなかった。ステージング実測の形 — D-158 2026-10-06 実測・先方投入）
  robotError("FX-ERR-11", {
    robot_id: null,
    serial_number: "0",
    ...facilityOf(5),
    clean_timetable_id: TT_MAIN_B,
    error_type: "NOT_STARTED",
    error_level: "CRITICAL",
    errored_at: dbAt(1, "23:30"),
    created_at: dbAt(1, "23:31"),
  }),
];
