import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { readNotifGateSource } from "./notifGate";

const FIXTURE_PATH = join(import.meta.dir, "../../../test/fixtures/activity/notif-gate.jsonl");

describe("readNotifGateSource", () => {
  test("maps every attempt to a row carrying its own text", () => {
    const fixture = readFileSync(FIXTURE_PATH, "utf8");
    const { coverage, events } = readNotifGateSource({
      path: FIXTURE_PATH,
      readFileImpl: () => fixture,
    });

    expect(coverage).toEqual({
      id: "notif-gate",
      label: "alert gate decisions",
      category: "notification",
      path: FIXTURE_PATH,
      status: "ok",
      records: 3,
      totalRecords: 3,
      skipped: 3,
      earliest: new Date(1762483200 * 1000).toISOString(),
      latest: new Date(1762667000 * 1000).toISOString(),
    });

    expect(events).toHaveLength(3);
    expect(events[0]).toMatchObject({
      id: "notif-gate:1",
      category: "notification",
      source: "notif-gate",
      actor: "system",
      severity: "notice",
      title: "Disk space low — root is at 96% — 1.2G left",
    });
    expect(events[0]?.detail).toMatchObject({
      verdict: "blocked",
      source_app: "/home/user/Projects/overdeck/modules/monitor/bin/disk-fill-notify",
      title: "Disk space low",
      body: "root is at 96% — 1.2G left",
    });

    expect(events[1]).toMatchObject({
      id: "notif-gate:2",
      severity: "info",
      title: "Killed 2.1.222 — Freed memory from 2.1.222.",
      dedupeKey: "allowed:/home/user/Projects/overdeck/modules/monitor/bin/mem-pressure-notify:Killed #.#.#",
    });

    expect(events[2]).toMatchObject({
      id: "notif-gate:5",
      severity: "notice",
      title: "system-monitor guard DOWN",
    });
  });

  test("keeps coverage full-file while applying from/to windows", () => {
    const fixture = readFileSync(FIXTURE_PATH, "utf8");
    const { coverage, events } = readNotifGateSource({
      path: FIXTURE_PATH,
      readFileImpl: () => fixture,
      fromMs: 1762600000 * 1000,
    });

    expect(coverage.totalRecords).toBe(3);
    expect(coverage.records).toBe(2);
    expect(events.map((event) => event.id)).toEqual(["notif-gate:2", "notif-gate:5"]);
  });

  test("reports absent when the log has never been written", () => {
    const { coverage, events } = readNotifGateSource({
      path: "/nowhere/notif-log.jsonl",
      existsSyncImpl: () => false,
    });

    expect(coverage.status).toBe("absent");
    expect(coverage.records).toBe(0);
    expect(events).toEqual([]);
  });

  test("reports error instead of throwing when the log cannot be read", () => {
    const { coverage, events } = readNotifGateSource({
      path: "/root/notif-log.jsonl",
      existsSyncImpl: () => true,
      readFileImpl: () => {
        throw new Error("EACCES: permission denied");
      },
    });

    expect(coverage.status).toBe("error");
    expect(coverage.error).toBe("EACCES: permission denied");
    expect(events).toEqual([]);
  });

  test("skips corrupt lines without losing the sound ones", () => {
    const { coverage, events } = readNotifGateSource({
      path: "/tmp/notif-log.jsonl",
      existsSyncImpl: () => true,
      readFileImpl: () => [
        "{ this is not json",
        "[]",
        JSON.stringify({ timestamp: 1762483200, verdict: "blocked", title: "kept", body: "one" }),
      ].join("\n"),
    });

    expect(coverage.status).toBe("ok");
    expect(coverage.skipped).toBe(2);
    expect(events).toHaveLength(1);
    expect(events[0]?.title).toBe("kept — one");
  });

  test("keeps historical records queryable after the event schema change", () => {
    const { events } = readNotifGateSource({
      path: "/tmp/notif-log.jsonl",
      existsSyncImpl: () => true,
      readFileImpl: () => JSON.stringify({
        ts: 1762483200,
        decision: "suppressed",
        program: "/legacy/notifier",
        summary: "legacy title",
        body: "legacy body",
      }),
    });

    expect(events[0]).toMatchObject({
      severity: "notice",
      title: "legacy title — legacy body",
      dedupeKey: "blocked:/legacy/notifier:legacy title",
    });
  });
});
