import {
  CLEAN_TIMETABLE_ID_UNSCHEDULED,
  D_SEC_ALERT_ERROR_TYPES,
  ERROR_LIST_NOTE_REQUIRED_IDS,
  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）。
 *
 * ★20行（`FX-ERR-01`〜`20`）。そのうち見える行は18行★ — 既定（未解消・対応中）で15行
 *   （`FX-ERR-03` / `14` / `15` 以外）・「解消済みも表示」で18行。
 *   ★`FX-ERR-20` は `clean_timetable_id = 0`（予定外のタスク。TeamWorks 回答 2026-10-27）★ — 予定は「なし」（`none`）で出る。
 *   ★`FX-ERR-18`（削除済みの施設）・`19`（削除済みの清掃エリア）はどこにも出ない★（`isVisibleErrorRow()`。指示書AX U8・表5 V2/V6）。
 *
 * ★D-Sec アラート（`dsAlerts()`）もこの配列から作る★（`dsAlertsFromErrorRows()`。指示書AX D13）—
 *   `D-S_CONTROL_FAILURE` の行のうち見える7行（`FX-ERR-10`・`12`〜`17`。未解消3・対応中2・解消2）。
 *   ★`FX-ERR-19` は削除済みの清掃エリアなので出ない★。エラー一覧の上書き表を共有するので、
 *   `PATCH /errors/{error_id}/status` で書いた対応状況が D-Sec にも出る（U4）。
 *
 * ★★値はダミー★★:
 *   - 実在の店名・住所を入れない（`CLAUDE.md` §7）。★施設名は `facilities` から id で引く★（直書きしない）
 *   - ★`error_level` の値（`CRITICAL` 以外）と、`SECURITY_ON` の `error_level` は実データ未受領のダミー★。
 *     `NOT_STARTED`・`CRITICAL` の組はステージングの実測の形（D-158 2026-10-06 実測・先方投入）。
 *     `D-S_CONTROL_FAILURE` は `CRITICAL`（第10回 N-5）
 *   - ★セキュリティ・清掃開始失敗のシリアルは `---`（`SERIAL_NOT_APPLICABLE`）★
 *   - `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）。
 *
 * ★エラー内容（`error_list_id`。指示書BD §5-2。D-184）★ — ★大半は null（未選択）★。値があるのは次の5行だけ:
 *   - `FX-ERR-01`（EmergencyStop）= 40「非常停止スイッチが作動しました」／`FX-ERR-03`（HandleUp・解消済み）= 39「ハンドルが持ち上げられました」
 *     ／`FX-ERR-06`（Offline）= 37「ロボットがオフラインです」 — ★有効な id★（`lib/fixtures/error-lists.ts` にある）
 *   - `FX-ERR-11`（★`NOT_STARTED` — 選べる行★）= ★131「その他(該当なし)」＋備考あり★（`ERROR_LIST_NOTE_REQUIRED_IDS`）
 *   - `FX-ERR-05`（BrushError）= ★9999 — `error_lists` に無い id★（`error_list: null`・`error_list_id: 9999`。画面は「不明(9999)」）
 *   ★選べない行（`D-S_CONTROL_FAILURE`・`SECURITY_ON`）には入れない★（`ERROR_LIST_UNSELECTABLE_ERROR_TYPES`）。
 *   ★D-Sec の行はローカルDBにも投入される★（`scripts/seed-local-db.ts`）。シードも選べない行には値を入れない（入れようとすると止まる）。
 *   ローカルDBの `error_lists` 表と `error_list_id` 列は `scripts/local-db/` の手順で足す（指示書BD §8）。
 */

/**
 * fixtures の行（★`robot_id` は読まない。実データの形を示すためだけ★）。
 * ★`section` は全行に持たせる★（実DBでも `section_id` は NOT NULL）。`section_name` は D-Sec アラートが読む。
 */
export interface FixtureErrorReportRow extends ErrorReportRowSource {
  robot_id: bigint | null;
  section: { section_name: string; deleted_at: Date | null };
}

/**
 * セキュリティ・清掃開始失敗の行のシリアル。ステージングの実データ（2026-10-19 監督の SELECT・先方投入）。
 * P-8 の回答は `0` だが実値は `---`（指示書AX §12 申し送り2）。
 */
const SERIAL_NOT_APPLICABLE = "---";

/** 削除の日時（★過去の日時。`deleted_at` は NULL か否かだけで決まる★。指示書AX D12） */
const DELETED_AT = toDbJst(new Date("2026-10-01T10:00:00+09:00"));

// ★分数を直書きしないための別名（禁止事項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（`lib/fixtures/error-lists.ts` の決定稿の id。★値に意味がある★ので名前を付ける）。
 * ★`ERROR_LIST_OTHER` は `ERROR_LIST_NOTE_REQUIRED_IDS` から読む★（備考必須の id を直書きしない）。
 */
const ERROR_LIST_ROBOT_OFFLINE = BigInt(37);
const ERROR_LIST_HANDRAIL_LIFTED = BigInt(39);
const ERROR_LIST_EMERGENCY_STOP = BigInt(40);
const ERROR_LIST_OTHER = BigInt(ERROR_LIST_NOTE_REQUIRED_IDS[0]);
/** ★`error_lists` に無い id★（名前解決できない。`TT_MISSING` と同じ 9999 だが別の表の id） */
const ERROR_LIST_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; deleted_at: Date | null };
} {
  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, deleted_at: null } };
}

/**
 * ★削除済みのダミー施設★（表5 V2）。★`facilities`（`lib/fixtures/facilities.ts`）には足さない★ —
 *   施設一覧・分類・他の画面に出さないため。`facilityOf()` を通さない。
 */
function deletedDummyFacility(): {
  facility_id: bigint;
  facility: { facility_name: string; deleted_at: Date | null };
} {
  return {
    facility_id: BigInt(99),
    facility: { facility_name: "削除済みテスト施設（ダミー）", deleted_at: DELETED_AT },
  };
}

/** その施設の先頭のロボットのシリアル（直書きしない） */
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),
    section: { section_name: "1F売り場", deleted_at: null },
    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,
    // ★既定は未選択★（指示書BD §5-2）
    error_list_id: null,
    ...overrides,
  };
}

/**
 * D-Sec アラートの行（制御盤のエラー）の既定値（指示書AX 段階3-1-3）。
 * ★`robot_id` NULL・`error_type` は設定から・`error_level` CRITICAL（第10回 N-5）・本清掃 A・未解消★。
 * ★シリアルは行の施設の制御盤★（直書きしない）。FX-ERR-16 のように食い違わせるときは `overrides` で上書きする。
 */
function dSecError(
  errorId: string,
  facilityId: number,
  overrides: Partial<FixtureErrorReportRow> = {}
): FixtureErrorReportRow {
  return robotError(errorId, {
    robot_id: null,
    serial_number: controlPanelSerialOf(facilityId),
    ...facilityOf(facilityId),
    error_type: D_SEC_ALERT_ERROR_TYPES[0],
    error_level: "CRITICAL",
    ...overrides,
  });
}

export const errorReportRows: FixtureErrorReportRow[] = [
  // E1: ロボットのエラー・本清掃に紐づく（未解消）。★エラー内容 = 有効な id（40）★
  robotError("FX-ERR-01", { error_list_id: ERROR_LIST_EMERGENCY_STOP }),
  // 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: "現場でハンドルを戻してもらった",
    // ★解消済みの行でもエラー内容を持つ★（有効な id）
    error_list_id: ERROR_LIST_HANDRAIL_LIFTED,
  }),
  // 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"),
    // ★`error_lists` に無い id★（`error_list: null`・`error_list_id` は 9999 のまま）
    error_list_id: ERROR_LIST_MISSING,
  }),
  // 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"),
    error_list_id: ERROR_LIST_ROBOT_OFFLINE,
  }),
  // 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 '---'・error_type SECURITY_ON★（セキュリティのエラー。D-158 P-8）。
  //   ★D-Sec アラートには出ない★（指示書AX U1）
  robotError("FX-ERR-08", {
    robot_id: null,
    serial_number: SERIAL_NOT_APPLICABLE,
    ...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★（制御盤のエラー）。
  //   ★D-Sec アラート（指示書AX 表1 R1）★
  robotError("FX-ERR-10", {
    robot_id: null,
    serial_number: controlPanelSerialOf(1),
    clean_timetable_id: TT_MAIN_A,
    error_type: D_SEC_ALERT_ERROR_TYPES[0],
    error_level: "CRITICAL",
    errored_at: dbAt(1, "22:45"),
    created_at: dbAt(1, "22:46"),
  }),
  // ★robot_id NULL・serial_number '---'・error_type NOT_STARTED・error_level CRITICAL★
  //   （清掃を開始できなかった。ステージング実測の形 — D-158 2026-10-06 実測・先方投入）。
  //   ★D-Sec アラートには出ない★（指示書AX U2）
  robotError("FX-ERR-11", {
    robot_id: null,
    serial_number: SERIAL_NOT_APPLICABLE,
    ...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"),
    // ★清掃開始失敗の行は選べる★（指示書BD §0 の決定）。★その他（備考必須）＋備考あり★
    error_list_id: ERROR_LIST_OTHER,
    comment: "店舗の閉店作業が長引き、清掃開始時刻に通路が塞がっていた（ダミー）",
  }),

  // ---- D-Sec アラート（制御盤のエラー。指示書AX 段階3-1-3・表1）----
  // R4・R13: ★errored_at NULL（検知時刻は created_at）・clean_timetable_id NULL★
  dSecError("FX-ERR-12", 11, {
    clean_timetable_id: null,
    section: { section_name: "2F衣料品", deleted_at: null },
    errored_at: null,
    created_at: dbAt(0, "02:55"),
  }),
  // R2: 対応中・備考あり
  dSecError("FX-ERR-13", 5, {
    clean_timetable_id: TT_MAIN_B,
    section: { section_name: "1Fレジ前", deleted_at: null },
    errored_at: dbAt(1, "23:10"),
    created_at: dbAt(1, "23:11"),
    status: 1,
    responded_at: dbAt(0, "09:05"),
    comment: "回線業者に確認中",
  }),
  // R3: 解消
  dSecError("FX-ERR-14", 4, {
    section: { section_name: "B1食品", deleted_at: null },
    errored_at: dbAt(1, "03:55"),
    created_at: dbAt(1, "03:56"),
    status: 2,
    responded_at: dbAt(1, "08:40"),
    resolved_at: dbAt(1, "09:15"),
    comment: "正常復帰を確認",
  }),
  // R3: 解消（2日前）
  dSecError("FX-ERR-15", 10, {
    clean_timetable_id: TT_MAIN_B,
    section: { section_name: "1Fレジ前", deleted_at: null },
    errored_at: dbAt(2, "02:00"),
    created_at: dbAt(2, "02:01"),
    status: 2,
    responded_at: dbAt(2, "08:20"),
    resolved_at: dbAt(2, "08:30"),
    comment: "正常復帰を確認",
  }),
  // R11: ★シリアルは施設2の制御盤なのに、行の施設は3★（ステージング 1000004 の形。行の施設で出す。U3）
  dSecError("FX-ERR-16", 3, {
    serial_number: controlPanelSerialOf(2),
    errored_at: dbAt(0, "01:30"),
    created_at: dbAt(0, "01:31"),
  }),
  // R12: ★FX-ERR-16 と同じ検知時刻★（同値は id の辞書順で "FX-ERR-16" が先）
  dSecError("FX-ERR-17", 8, {
    clean_timetable_id: TT_MAIN_B,
    errored_at: dbAt(0, "01:30"),
    created_at: dbAt(0, "01:32"),
    status: 1,
    responded_at: dbAt(0, "08:50"),
  }),

  // ---- 削除済みの施設・清掃エリア（指示書AX U8・表5）。★どちらも status 0 — 可視性が無ければ既定で出るはずの行★ ----
  // V2: ★施設が削除済み★（清掃エリアは生きている）・ロボットのエラー
  robotError("FX-ERR-18", {
    robot_id: BigInt(199),
    serial_number: "DUMMY-DELETED-ROBOT", // ★ダミー施設は facilities に無いので、ロボットのシリアルも引けない★
    ...deletedDummyFacility(),
    error_type: "EmergencyStop",
    errored_at: dbAt(1, "23:20"),
    created_at: dbAt(1, "23:21"),
  }),
  // V6: ★清掃エリアが削除済み★（施設2は生きている）・D-S_CONTROL_FAILURE。
  //   ★施設2の他の行（FX-ERR-04・05）の清掃エリアは生きているので出続ける（表5 V9）★
  dSecError("FX-ERR-19", 2, {
    section: { section_name: "旧2F売り場（削除済み）", deleted_at: DELETED_AT },
    errored_at: dbAt(0, "02:10"),
    created_at: dbAt(0, "02:11"),
  }),

  // ---- clean_timetable_id = 0（予定外のタスク。TeamWorks 回答 2026-10-27）----
  // E4 と同じく ★予定は「なし」（`none`）・API の `clean_timetable_id` は null★（`scheduledTimetableId()`）。
  //   ★「予定不明」（`unknown`）に化けないこと★（0 は本表にも履歴にも無い id なので、`=== null` だけで判定すると化ける）
  robotError("FX-ERR-20", {
    robot_id: BigInt(110),
    serial_number: robotSerialOf(6),
    ...facilityOf(6),
    // ★`0` を直書きしない★（`CLEAN_TIMETABLE_ID_UNSCHEDULED`）
    clean_timetable_id: BigInt(CLEAN_TIMETABLE_ID_UNSCHEDULED),
    error_type: "LostLocation",
    errored_at: dbAt(1, "14:40"),
    created_at: dbAt(1, "14:41"),
  }),
];
