import { describe, expect, it } from "vitest";

import {
  CLEAN_AREA_ANOMALY_METRICS,
  reportCleanAreaAnomalies,
} from "@/lib/clean-area/anomaly";
import type { DataAnomalyLogEvent } from "@/lib/logger/events";

/**
 * 指示書AN ★段階3★ §2-5 / §6-3（★落とし穴2★）— 番人のログ。
 *
 * ★★静かに壊れる代わりにログに出す★★のが目的なので、
 *   ★「異常があるのに1行も出ない」★ことをテストで止める。
 */

function collect(): { events: DataAnomalyLogEvent[]; emit: (e: DataAnomalyLogEvent) => void } {
  const events: DataAnomalyLogEvent[] = [];
  return { events, emit: (event) => events.push(event) };
}

describe("CLEAN_AREA_ANOMALY_METRICS — ログの `metric` の綴りを固定する", () => {
  // ★ログの `metric` は外部（監視・集計）との契約★。他のテストは定数を参照するので、
  //   ★綴りがうっかり変わっても1件も落ちない★。ここだけは文字列そのものを固定する。
  //   ★変えるときは、この名前で警告を拾っている監視が無いことを確かめてから★（D-151）。
  it("重複検知は clean_task_reports の名前（2026-10-01 に旧テーブル名から改名）", () => {
    expect(CLEAN_AREA_ANOMALY_METRICS.duplicatedTimetables).toBe(
      "clean_task_report_rows_per_timetable"
    );
  });

  it("施設マスタに無い facility_id", () => {
    expect(CLEAN_AREA_ANOMALY_METRICS.unmatchedFacilities).toBe(
      "clean_area_facility_ids_without_master"
    );
  });
});

describe("reportCleanAreaAnomalies", () => {
  it("異常が無ければ1行も出さない（0件を毎回出すとログが埋まる）", () => {
    const { events, emit } = collect();
    reportCleanAreaAnomalies(
      {
        route: "/api/clean-area",
        method: "GET",
        duplicated_timetable_count: 0,
        unmatched_facility_ids: [],
      },
      emit
    );
    expect(events).toEqual([]);
  });

  it("★★履歴が2行以上ある清掃があれば warn を出す★★（期待値0と実測だけを残す）", () => {
    const { events, emit } = collect();
    reportCleanAreaAnomalies(
      {
        route: "/api/clean-area",
        method: "GET",
        duplicated_timetable_count: 3,
        unmatched_facility_ids: [],
      },
      emit
    );
    expect(events).toEqual([
      {
        event: "data.anomaly",
        route: "/api/clean-area",
        method: "GET",
        metric: CLEAN_AREA_ANOMALY_METRICS.duplicatedTimetables,
        expected: 0,
        actual: 3,
      },
    ]);
  });

  it("施設マスタに無い `facility_id` も同じ形で残す（★件数だけ。IDは載せない★）", () => {
    const { events, emit } = collect();
    reportCleanAreaAnomalies(
      {
        route: "/api/clean-area",
        method: "GET",
        duplicated_timetable_count: 0,
        unmatched_facility_ids: [98, 99],
      },
      emit
    );
    expect(events).toHaveLength(1);
    expect(events[0].metric).toBe(CLEAN_AREA_ANOMALY_METRICS.unmatchedFacilities);
    expect(events[0].actual).toBe(2);
  });

  it("両方あれば2行出す（★片方で打ち切らない★）", () => {
    const { events, emit } = collect();
    reportCleanAreaAnomalies(
      {
        route: "/api/clean-area",
        method: "GET",
        duplicated_timetable_count: 1,
        unmatched_facility_ids: [7],
      },
      emit
    );
    expect(events).toHaveLength(2);
  });

  it("★`metric` の文字列に原因の断定を書かない★（`docs/logging-design.md` §7）", () => {
    // ★「TeamWorks の収集処理が二重登録している」のような推測を metric に埋め込まない★ —
    //   ★テーブル名と数え方だけ★（`clean_log_rows_vs_scheduled`（D-049）と同じ流儀）
    for (const metric of Object.values(CLEAN_AREA_ANOMALY_METRICS)) {
      expect(metric).toMatch(/^[a-z0-9_]+$/);
    }
  });
});
