import { describe, test, expect } from "bun:test";
import { AuthChecker } from "@/health/auth-checker.js";
import { FakeMetrics } from "../_helpers/fake-metrics.js";
import { mockLogger } from "../_helpers/mock-logger.js";
import { METRIC } from "@/metrics/names.js";

describe("AuthChecker", () => {
  // 1. Healthy probe emits gauge 1 with integration tag
  test("healthy probe emits integration.auth_ok gauge 1 with integration tag", async () => {
    const metrics = new FakeMetrics();
    const logger = mockLogger();
    const checker = new AuthChecker({
      metrics,
      logger,
      intervalMs: 60_000,
      probes: [{ integration: "jira", check: async () => true }],
    });

    await checker.checkAll();

    const gaugeCall = metrics.last(METRIC.integrationAuthOk);
    expect(gaugeCall).toBeDefined();
    expect(gaugeCall!.kind).toBe("gauge");
    expect(gaugeCall!.value).toBe(1);
    expect(gaugeCall!.tags).toEqual({ integration: "jira" });
  });

  // 2. ok->fail transition: gauge 0 + logger.error called
  test("ok->fail transition emits gauge 0 and logs error", async () => {
    const metrics = new FakeMetrics();
    const logger = mockLogger();
    let healthy = true;
    const checker = new AuthChecker({
      metrics,
      logger,
      intervalMs: 60_000,
      probes: [{ integration: "github", check: async () => healthy }],
    });

    // First call: ok (prev was undefined → no log; first-call success is not a recovery)
    await checker.checkAll();

    // Flip to failing
    healthy = false;

    // Second call: fail → transition ok->fail, emits gauge 0, logs error
    await checker.checkAll();

    const gaugeCalls = metrics.calls.filter((c) => c.name === METRIC.integrationAuthOk);
    expect(gaugeCalls).toHaveLength(2);
    expect(gaugeCalls[1]!.value).toBe(0);
    expect(gaugeCalls[1]!.tags).toEqual({ integration: "github" });
    expect(logger.error).toHaveBeenCalledWith({ integration: "github" }, "integration_auth_failed");
  });

  // 3. First-call success does NOT log integration_auth_recovered (no phantom recovery)
  test("first-call success does not log integration_auth_recovered", async () => {
    const metrics = new FakeMetrics();
    const logger = mockLogger();
    const checker = new AuthChecker({
      metrics,
      logger,
      intervalMs: 60_000,
      probes: [{ integration: "jira", check: async () => true }],
    });

    await checker.checkAll();

    expect(logger.info).not.toHaveBeenCalledWith(
      { integration: "jira" },
      "integration_auth_recovered",
    );
  });

  // 4. First-call failure DOES log integration_auth_failed (boot failures stay loud)
  test("first-call failure logs integration_auth_failed", async () => {
    const metrics = new FakeMetrics();
    const logger = mockLogger();
    const checker = new AuthChecker({
      metrics,
      logger,
      intervalMs: 60_000,
      probes: [{ integration: "slack", check: async () => false }],
    });

    await checker.checkAll();

    expect(logger.error).toHaveBeenCalledWith({ integration: "slack" }, "integration_auth_failed");
  });

  // 5. Throwing probe counts as 0 and does not crash the loop; both probes emit
  test("throwing probe emits gauge 0 and does not crash; healthy probe emits gauge 1", async () => {
    const metrics = new FakeMetrics();
    const logger = mockLogger();
    const checker = new AuthChecker({
      metrics,
      logger,
      intervalMs: 60_000,
      probes: [
        { integration: "jira", check: async () => true },
        {
          integration: "slack",
          check: async () => {
            throw new Error("auth timeout");
          },
        },
      ],
    });

    await checker.checkAll();

    const jiraGauge = metrics.calls.find(
      (c) => c.name === METRIC.integrationAuthOk && c.tags?.integration === "jira",
    );
    const slackGauge = metrics.calls.find(
      (c) => c.name === METRIC.integrationAuthOk && c.tags?.integration === "slack",
    );

    expect(jiraGauge).toBeDefined();
    expect(jiraGauge!.value).toBe(1);

    expect(slackGauge).toBeDefined();
    expect(slackGauge!.value).toBe(0);
  });
});
