import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

import { PATCH } from "@/app/api/prechecks/[id]/status/route";
import { GET } from "@/app/api/prechecks/route";
import { resetFixturePrecheckOverrides } from "@/lib/db/fixture-precheck-overrides";
import { prechecks } from "@/lib/fixtures/prechecks";
import type { PrecheckItem, SummaryListResponse } from "@/lib/types";
import type { PrecheckSummary } from "@/lib/prechecks/query";

/**
 * `PATCH /prechecks/{id}/status`（`USE_FIXTURES=true`。指示書AT 段階4-3 / §5）。
 *
 * ★★なぜ別ファイルなのか★★ — `lib/db/index.ts` の `db` はモジュール読み込み時に束縛されるので、
 * 同じファイルで true/false の両方は試せない（`app/api/ds-devices/[serial_number]/note/__tests__/` と同じ）。
 *
 * fixtures 版は `lib/fixtures/prechecks.ts` を書き換えず、モジュール内の上書き表を持つ。
 * ★テストごとに `resetFixturePrecheckOverrides()` で空に戻す★。
 */

vi.mock("@/lib/db/use-fixtures", () => ({ USE_FIXTURES: true }));

vi.mock("@/lib/logger", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@/lib/logger")>();
  return { ...actual, logEvent: vi.fn() };
});

const NOW_ISO = "2026-10-02T08:15:00+09:00";

beforeEach(() => {
  resetFixturePrecheckOverrides();
  vi.useFakeTimers({ toFake: ["Date"] });
  vi.setSystemTime(new Date(NOW_ISO));
});

afterEach(() => {
  vi.useRealTimers();
  resetFixturePrecheckOverrides();
});

async function patch(id: string, body: unknown): Promise<Response> {
  return PATCH(
    new Request(`http://localhost/api/prechecks/${encodeURIComponent(id)}/status`, {
      method: "PATCH",
      body: JSON.stringify(body),
    }),
    { params: Promise.resolve({ id }) }
  );
}

async function listAll(): Promise<SummaryListResponse<PrecheckItem, PrecheckSummary>> {
  const res = await GET(new Request("http://localhost/api/prechecks?status=all&per_page=100"));
  expect(res.status).toBe(200);
  return (await res.json()) as SummaryListResponse<PrecheckItem, PrecheckSummary>;
}

describe("PATCH /prechecks/{id}/status（USE_FIXTURES=true）", () => {
  it("★前提★: id \"1\" は未解消の事前チェック。\"4\"・\"8\"（旧: 出発失敗）は無い（2026-10-29 に消した）", () => {
    const byId = new Map(prechecks.map((p) => [p.id, p]));
    expect(byId.get("1")).toMatchObject({ severity: "precheck", resolution_status: "unresolved" });
    expect(byId.has("4")).toBe(false);
    expect(byId.has("8")).toBe(false);
  });

  it("PATCH → GET /prechecks に反映される（状態・日時・備考とサマリ）", async () => {
    const before = await listAll();

    const res = await patch("1", { status: "resolved", note: "  現場で解消  " });
    expect(res.status).toBe(200);
    const updated = (await res.json()) as PrecheckItem;
    expect(updated).toMatchObject({
      id: "1",
      resolution_status: "resolved",
      responded_at: NOW_ISO,
      resolved_at: NOW_ISO,
      note: "現場で解消",
    });

    const after = await listAll();
    expect(after.items.find((p) => p.id === "1")).toEqual(updated);
    expect(after.summary).toEqual({
      unresolved: before.summary.unresolved - 1,
      resolved: before.summary.resolved + 1,
      total: before.summary.total,
    });
  });

  it("戻すと日時を消し、備考を空にすると null になる（元の4項目に戻せる）", async () => {
    const original = prechecks.find((p) => p.id === "9");
    expect(original).toMatchObject({ resolution_status: "unresolved", note: null });

    await patch("9", { status: "in_progress", note: "連絡済み" });
    await patch("9", { status: "resolved" });
    const res = await patch("9", { status: "unresolved", note: "" });
    expect(res.status).toBe(200);
    const back = (await res.json()) as PrecheckItem;
    expect(back).toEqual(original);
  });

  it("★元の fixtures 配列は書き換えない★", async () => {
    const snapshot = structuredClone(prechecks);
    await patch("1", { status: "in_progress" });
    expect(prechecks).toEqual(snapshot);
  });

  it.each(["4", "8"])("★旧 `launch_failed` の id（%s）は 404★（行が無い）", async (id) => {
    const res = await patch(id, { status: "resolved" });
    expect(res.status).toBe(404);
  });

  it("存在しない id は 404", async () => {
    const res = await patch("RER-NOT-EXIST", { status: "resolved" });
    expect(res.status).toBe(404);
  });
});
