import { execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";

function validAssessment() {
  return {
    createdAt: "2026-07-09T12:00:00.000Z",
    goal100Decision: "qualified: winner=current",
    goal100: {
      limit: 100,
      singleEngine: true,
      winnerEngine: "current",
      winnerQualified: true,
      winnerActiveAverageMsPerParsed: 393,
      winnerActiveProjectedMsFor100: 39300,
      winnerAverageCaptureRoundTripMs: 333,
      winnerAverageCaptureRoundTripOverheadMs: 148,
      winnerMissRate: 0,
      winnerReviewRate: 0,
      engines: [
        { engine: "current", qualified: true, missRate: 0, reviewRate: 0, averageCaptureRoundTripMs: 333, averageCaptureRoundTripOverheadMs: 148 },
      ],
    },
    limits: [
      {
        limit: 20,
        singleEngine: true,
        winnerEngine: "current",
        winnerQualified: true,
        winnerActiveAverageMsPerParsed: 390,
        winnerActiveProjectedMsFor100: 39000,
        winnerAverageCaptureRoundTripMs: 364,
        winnerAverageCaptureRoundTripOverheadMs: 152,
        winnerMissRate: 0,
        winnerReviewRate: 0.05,
        engines: [
          { engine: "current", qualified: true, missRate: 0, reviewRate: 0.05, averageCaptureRoundTripMs: 364, averageCaptureRoundTripOverheadMs: 152 },
        ],
      },
    ],
  };
}

function writeAssessment(dir: string, payload: unknown) {
  const inputPath = path.join(dir, "scan-performance-assessment.json");
  writeFileSync(inputPath, JSON.stringify(payload), "utf8");
  return inputPath;
}

describe("scan assessment validator", () => {
  it("accepts a qualified 100-artifact current run", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      const output = execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`], {
        cwd: process.cwd(),
        encoding: "utf8",
      });
      expect(JSON.parse(output).ok).toBe(true);
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("accepts a matching expected winner", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      const output = execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--expect-winner=current"], {
        cwd: process.cwd(),
        encoding: "utf8",
      });
      expect(JSON.parse(output).ok).toBe(true);
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("prints an opt-in human summary", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      const output = execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary"], {
        cwd: process.cwd(),
        encoding: "utf8",
      });
      expect(output).toContain("scan assessment: PASS");
      expect(output).toContain(`input: ${inputPath}`);
      expect(output).toContain("createdAt: 2026-07-09T12:00:00.000Z");
      expect(output).toContain("limit: 100");
      expect(output).toContain("winner: current");
      expect(output).toContain("activeAvg: 393ms/artifact");
      expect(output).toContain("captureRoundTripOverhead: 148ms");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("accepts a qualified 20-artifact current run", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      const output = execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary", "--limit=20"], {
        cwd: process.cwd(),
        encoding: "utf8",
      });
      expect(output).toContain("scan assessment: PASS");
      expect(output).toContain("limit: 20");
      expect(output).toContain("winner: current");
      expect(output).toContain("activeAvg: 390ms/artifact");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("accepts explicit speed and capture-overhead budgets when the winner stays inside them", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      const output = execFileSync(
        "node",
        [
          "scripts/validate-scan-assessment.cjs",
          `--input=${inputPath}`,
          "--summary",
          "--limit=20",
          "--max-active-average-ms=400",
          "--max-capture-roundtrip-overhead-ms=160",
        ],
        {
          cwd: process.cwd(),
          encoding: "utf8",
        },
      );
      expect(output).toContain("scan assessment: PASS");
      expect(output).toContain("captureRoundTripOverhead: 152ms");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects speed and capture-overhead budgets when the winner exceeds them", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      let stdout = "";
      try {
        execFileSync(
          "node",
          [
            "scripts/validate-scan-assessment.cjs",
            `--input=${inputPath}`,
            "--summary",
            "--limit=20",
            "--max-active-average-ms=333",
            "--max-capture-roundtrip-overhead-ms=120",
          ],
          {
            cwd: process.cwd(),
            encoding: "utf8",
            stdio: "pipe",
          },
        );
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("Winner active average timing exceeds 333ms");
      expect(stdout).toContain("Winner capture roundtrip overhead exceeds 120ms");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects invalid requested limits", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      let stdout = "";
      try {
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary", "--limit=0"], {
          cwd: process.cwd(),
          encoding: "utf8",
          stdio: "pipe",
        });
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("--limit must be a positive integer");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects a requested limit that is missing from the assessment", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      let stdout = "";
      try {
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary", "--limit=45"], {
          cwd: process.cwd(),
          encoding: "utf8",
          stdio: "pipe",
        });
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("Missing limit=45 assessment");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("keeps the validated npm scripts wired through preflight and the correct assessment limit", () => {
    const packageJson = JSON.parse(readFileSync(path.join(process.cwd(), "package.json"), "utf8"));
    expect(packageJson.scripts["scan:goal:validated"]).toBe(
      "npm run scan:live:preflight && npm run scan:goal && npm run scan:assessment:validate -- --latest --summary",
    );
    expect(packageJson.scripts["scan:goal:validated:wait"]).toBe(
      "npm run scan:live:preflight:wait && npm run scan:goal && npm run scan:assessment:validate -- --latest --summary",
    );
    expect(packageJson.scripts["scan:iterate"]).toBe(
      "powershell -NoProfile -ExecutionPolicy Bypass -File scripts\\live-soak.ps1 -Limits 20 -BenchmarkOcr",
    );
    expect(packageJson.scripts["scan:iterate:validated"]).toBe(
      "npm run scan:live:preflight && npm run scan:iterate && npm run scan:assessment:validate -- --latest --summary --limit=20",
    );
    expect(packageJson.scripts["scan:iterate:validated:wait"]).toBe(
      "npm run scan:live:preflight:wait && npm run scan:iterate && npm run scan:assessment:validate -- --latest --summary --limit=20",
    );
    expect(packageJson.scripts["scan:repeatability"]).toBe(
      "powershell -NoProfile -ExecutionPolicy Bypass -File scripts\\live-soak.ps1 -RepeatabilityRun -ScanEngine current",
    );
    expect(packageJson.scripts["scan:repeatability:wait"]).toBe(
      "npm run scan:live:preflight:wait && npm run scan:repeatability && npm run scan:assessment:validate -- --latest --summary --limit=100 --expect-winner=current",
    );
  });

  it("rejects an invalid expected winner", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const inputPath = writeAssessment(dir, validAssessment());
      expect(() =>
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--expect-winner=old-engine"], {
          cwd: process.cwd(),
          stdio: "pipe",
        }),
      ).toThrow();
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("prints errors in summary mode for rejected assessments", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const payload = validAssessment();
      payload.goal100Decision = "not-qualified: 100-artifact winner failed quality gates";
      const inputPath = writeAssessment(dir, payload);
      let stdout = "";
      try {
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary"], {
          cwd: process.cwd(),
          encoding: "utf8",
          stdio: "pipe",
        });
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("goal100Decision is not a qualified 100-artifact run");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects an unqualified winner", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const payload = validAssessment();
      payload.goal100Decision = "not-qualified: 100-artifact winner failed quality gates";
      payload.goal100.winnerQualified = false;
      payload.goal100.engines[0].qualified = false;
      payload.goal100.engines[0].reviewRate = 0.3;
      const inputPath = writeAssessment(dir, payload);
      expect(() =>
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`], {
          cwd: process.cwd(),
          stdio: "pipe",
        }),
      ).toThrow();
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects a winner with missing quality rates", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const payload = validAssessment();
      delete (payload.goal100.engines[0] as { missRate?: number }).missRate;
      delete (payload.goal100.engines[0] as { reviewRate?: number }).reviewRate;
      const inputPath = writeAssessment(dir, payload);
      let stdout = "";
      try {
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary"], {
          cwd: process.cwd(),
          encoding: "utf8",
          stdio: "pipe",
        });
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("Winner missRate must be a finite number");
      expect(stdout).toContain("Winner reviewRate must be a finite number");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects a winner with missing summary quality rates", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const payload = validAssessment();
      delete (payload.goal100 as { winnerMissRate?: number }).winnerMissRate;
      delete (payload.goal100 as { winnerReviewRate?: number }).winnerReviewRate;
      const inputPath = writeAssessment(dir, payload);
      let stdout = "";
      try {
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary"], {
          cwd: process.cwd(),
          encoding: "utf8",
          stdio: "pipe",
        });
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("Winner summary missRate must be a finite number");
      expect(stdout).toContain("Winner summary reviewRate must be a finite number");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("rejects a winner with missing speed timing", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const payload = validAssessment();
      delete (payload.goal100 as { winnerActiveAverageMsPerParsed?: number }).winnerActiveAverageMsPerParsed;
      delete (payload.goal100 as { winnerActiveProjectedMsFor100?: number }).winnerActiveProjectedMsFor100;
      const inputPath = writeAssessment(dir, payload);
      let stdout = "";
      try {
        execFileSync("node", ["scripts/validate-scan-assessment.cjs", `--input=${inputPath}`, "--summary"], {
          cwd: process.cwd(),
          encoding: "utf8",
          stdio: "pipe",
        });
      } catch (error) {
        stdout = String((error as { stdout?: string }).stdout || "");
      }
      expect(stdout).toContain("scan assessment: FAIL");
      expect(stdout).toContain("Winner active average timing must be a positive finite number");
      expect(stdout).toContain("Winner projected100 timing must be a positive finite number");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it("validates the latest assessment under a live-soak root", () => {
    const dir = mkdtempSync(path.join(tmpdir(), "gaa-assessment-"));
    try {
      const older = path.join(dir, "2026-07-09T10-00-00");
      const newer = path.join(dir, "2026-07-09T11-00-00");
      mkdirSync(older);
      mkdirSync(newer);
      writeAssessment(older, { goal100Decision: "not-run: missing 100-artifact assessment" });
      writeAssessment(newer, validAssessment());

      const output = execFileSync("node", ["scripts/validate-scan-assessment.cjs", "--latest", `--root=${dir}`], {
        cwd: process.cwd(),
        encoding: "utf8",
      });
      const parsed = JSON.parse(output);
      expect(parsed.ok).toBe(true);
      expect(parsed.inputPath).toContain("2026-07-09T11-00-00");
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });
});
