/**
 * runGateTestScan availability handling.
 *
 * A GateTest outage or misconfiguration (non-2xx response, network error)
 * must not hard-block merges platform-wide — that makes every merge
 * hostage to a third-party service's uptime. Only an actual scan verdict
 * (a 200 response with a pass/fail result) should block. This mirrors the
 * "not configured" path, which already skips rather than fails.
 *
 * Regression coverage for the 2026-07-15 incident: gatetest.ai returned
 * 503 (missing GLUECRON_EMITTER_SECRET on its side) and every merge on
 * every repo failed with "GateTest returned 503: ...".
 */

import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { runGateTestScan } from "../lib/gate";
import { config } from "../lib/config";

const origFetch = globalThis.fetch;
const origUrl = process.env.GATETEST_URL;

function mockFetch(responder: () => Response | Promise<Response>): void {
  // @ts-expect-error — override global fetch for the test
  globalThis.fetch = async (): Promise<Response> => responder();
}

beforeEach(() => {
  process.env.GATETEST_URL = "https://gatetest.example/api/events/push";
});

afterEach(() => {
  globalThis.fetch = origFetch;
  if (origUrl === undefined) delete process.env.GATETEST_URL;
  else process.env.GATETEST_URL = origUrl;
});

describe("runGateTestScan — availability vs verdict", () => {
  it("skips (does not block) when GateTest is not configured", async () => {
    delete process.env.GATETEST_URL;
    const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
    expect(result.skipped).toBe(true);
    expect(result.passed).toBe(true);
  });

  it("skips (does not block) on a non-2xx response — e.g. GateTest itself is down", async () => {
    mockFetch(() => new Response(JSON.stringify({ error: "GLUECRON_EMITTER_SECRET is not set" }), { status: 503 }));
    const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
    expect(result.skipped).toBe(true);
    expect(result.passed).toBe(true);
    expect(result.details).toContain("503");
  });

  it("skips (does not block) when the request throws — network error / timeout", async () => {
    mockFetch(() => {
      throw new Error("ECONNREFUSED");
    });
    const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
    expect(result.skipped).toBe(true);
    expect(result.passed).toBe(true);
    expect(result.details).toContain("ECONNREFUSED");
  });

  it("blocks on a genuine 200 scan-fail verdict", async () => {
    mockFetch(() => new Response(JSON.stringify({ passed: false, summary: "2 vulnerabilities found" }), { status: 200 }));
    const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
    expect(result.skipped).toBeFalsy();
    expect(result.passed).toBe(false);
    expect(result.details).toBe("2 vulnerabilities found");
  });

  it("passes on a genuine 200 scan-pass verdict", async () => {
    mockFetch(() => new Response(JSON.stringify({ passed: true, summary: "All checks passed" }), { status: 200 }));
    const result = await runGateTestScan("o", "r", "refs/heads/main", "a".repeat(40));
    expect(result.skipped).toBeFalsy();
    expect(result.passed).toBe(true);
  });
});

// Keep config import referenced — sanity that config.gatetestUrl reflects env for this test file.
describe("config.gatetestUrl reflects GATETEST_URL for this suite", () => {
  it("resolves to the mocked value while set", () => {
    expect(config.gatetestUrl).toBe("https://gatetest.example/api/events/push");
  });
});
