import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
  classifyVulnerabilityFindings,
  renderDependencyVulnerabilityGateMarkdownReport,
  runDependencyVulnerabilityGate,
} from "../../scripts/dependency-vulnerability-gate.mjs";

function advisory({
  id,
  severity,
  title,
  vulnerableVersions = "<=1.0.0",
}: {
  id: string;
  severity: string;
  title: string;
  vulnerableVersions?: string;
}) {
  return {
    id,
    severity,
    title,
    vulnerable_versions: vulnerableVersions,
    url: `https://github.com/advisories/${id}`,
  };
}

async function writeLockfile(rootDir: string) {
  await writeFile(
    path.join(rootDir, "pnpm-lock.yaml"),
    `lockfileVersion: '9.0'

importers:
  .:
    dependencies:
      runtime-high:
        version: 1.0.0
    devDependencies:
      dev-high:
        version: 1.0.0

snapshots:
  runtime-high@1.0.0: {}
  dev-high@1.0.0: {}
  transitive-critical@1.0.0: {}
`,
    "utf8",
  );
}

describe("dependency-vulnerability-gate", () => {
  it("blocks critical advisories anywhere and high advisories in the production graph", () => {
    const result = classifyVulnerabilityFindings({
      allAdvisories: {
        "dev-high": [advisory({ id: "GHSA-dev-high", severity: "high", title: "dev high" })],
        "transitive-critical": [
          advisory({ id: "GHSA-critical", severity: "critical", title: "critical issue" }),
        ],
      },
      productionAdvisories: {
        "runtime-high": [
          advisory({ id: "GHSA-runtime-high", severity: "high", title: "runtime high" }),
        ],
      },
    });

    expect(result.blockers.map((finding) => finding.id)).toEqual([
      "GHSA-critical",
      "GHSA-runtime-high",
    ]);
    expect(result.findings.map((finding) => finding.id)).toEqual([
      "GHSA-critical",
      "GHSA-dev-high",
      "GHSA-runtime-high",
    ]);
  });

  it("blocks malware advisories regardless of severity or graph", () => {
    const result = classifyVulnerabilityFindings({
      allAdvisories: {
        dev: [advisory({ id: "GHSA-malware", severity: "low", title: "Malware in dev" })],
      },
      productionAdvisories: {},
    });

    expect(result.blockers).toMatchObject([
      {
        id: "GHSA-malware",
        malware: true,
        severity: "low",
      },
    ]);
  });

  it("queries full and production lockfile graphs separately", async () => {
    const rootDir = await mkdtemp(path.join(tmpdir(), "openclaw-vuln-gate-"));
    await writeLockfile(rootDir);
    const payloads: Record<string, string[]>[] = [];

    const report = await runDependencyVulnerabilityGate({
      rootDir,
      fetchImpl: async (_url, init) => {
        const payload = JSON.parse(String(init?.body));
        payloads.push(payload);
        const packages = Object.keys(payload);
        const body: Record<string, unknown[]> = {};
        if (packages.includes("runtime-high")) {
          body["runtime-high"] = [
            advisory({ id: "GHSA-runtime-high", severity: "high", title: "runtime high" }),
          ];
        }
        if (packages.includes("dev-high")) {
          body["dev-high"] = [
            advisory({ id: "GHSA-dev-high", severity: "high", title: "dev high" }),
          ];
        }
        return new Response(JSON.stringify(body), {
          status: 200,
          headers: { "content-type": "application/json" },
        });
      },
    });

    expect(payloads).toHaveLength(2);
    expect(payloads[0]).toEqual({
      "dev-high": ["1.0.0"],
      "runtime-high": ["1.0.0"],
      "transitive-critical": ["1.0.0"],
    });
    expect(payloads[1]).toEqual({
      "runtime-high": ["1.0.0"],
    });
    expect(report.blockers.map((finding) => finding.id)).toEqual(["GHSA-runtime-high"]);
    expect(report.findings.map((finding) => finding.id)).toEqual([
      "GHSA-dev-high",
      "GHSA-runtime-high",
    ]);
  });

  it("documents the resolved transitive dependency graph scope in Markdown", () => {
    const markdown = renderDependencyVulnerabilityGateMarkdownReport({
      generatedAt: "2026-05-12T00:00:00.000Z",
      policy: {
        blocks: [
          "known malware advisories anywhere in the installed graph",
          "critical advisories anywhere in the installed graph",
          "high advisories in the production/runtime graph",
        ],
        reports: [
          "moderate and lower advisories",
          "high advisories outside production/runtime graph",
        ],
        vulnerabilityExceptions: false,
      },
      graphs: {
        all: { packages: 2, packageVersions: 2 },
        production: { packages: 1, packageVersions: 1 },
      },
      blockers: [],
      findings: [],
    });

    expect(markdown).toContain("# npm Advisory Vulnerability Gate: Resolved Dependency Graph");
    expect(markdown).toContain("## Scope");
    expect(markdown).toContain("resolved package versions from pnpm-lock.yaml");
    expect(markdown).toContain("It includes transitive dependencies.");
  });
});
