import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { test } from "node:test";

async function text(path) {
  return readFile(new URL(`../${path}`, import.meta.url), "utf8");
}

test("required CI workflow gates pull requests with the root quality suite", async () => {
  const ci = await text(".github/workflows/ci.yml");

  assert.match(ci, /^name: CI/m);
  assert.match(ci, /pull_request:/);
  assert.match(ci, /workflow_call:/);
  assert.match(ci, /pnpm install --frozen-lockfile/);
  assert.match(ci, /pnpm ci:local/);
  assert.match(ci, /python -m pip install -r requirements\.txt/);
  assert.match(ci, /python -m pip install pytest/);
  assert.match(ci, /docker\/setup-buildx-action@v3/);
  assert.match(ci, /scripts\/ci-docker-build\.sh/);
  assert.match(ci, /Write quality gate summary/);
  assert.match(ci, /## Quality gate/);
  assert.match(ci, /GITHUB_STEP_SUMMARY/);
});

test("security audit fails on high advisories and uses explicit suppressions", async () => {
  const workflow = await text(".github/workflows/security-audit.yml");
  const suppressions = JSON.parse(await text("security/audit-suppressions.json"));

  assert.match(workflow, /pnpm audit:deps/);
  assert.match(workflow, /--audit-level high/);
  assert.ok(Array.isArray(suppressions.suppressions));
  assert.ok(suppressions.suppressions.length > 0);

  const keys = new Set();
  for (const suppression of suppressions.suppressions) {
    assert.match(suppression.id, /^GHSA-|^CVE-|^\d+$/);
    assert.ok(suppression.module);
    assert.ok(suppression.reason.includes("Temporary baseline suppression"));
    assert.equal(suppression.expires, "2026-07-19");
    assert.ok(Array.isArray(suppression.contexts));
    assert.ok(suppression.contexts.length > 0);
    for (const context of suppression.contexts) {
      assert.ok(
        ["production dependencies", "all dependencies"].includes(context)
      );
    }
    const key = `${suppression.module}:${suppression.id}`;
    assert.equal(keys.has(key), false, `duplicate suppression ${key}`);
    keys.add(key);
  }
});

test("deploy workflows are gated, immutable, scanned, signed, and environment protected", async () => {
  const workflow = await text(".github/workflows/backend-build.yml");

  assert.match(workflow, /concurrency:/);
  assert.match(workflow, /quality-gate:/);
  assert.match(workflow, /uses: \.\/\.github\/workflows\/ci\.yml/);
  assert.match(workflow, /needs: \[quality-gate, changes\]/);
  assert.match(workflow, /environment:/);
  assert.match(workflow, /Validate deployment configuration/);
  assert.match(workflow, /REQUIRED_AWS_ROLE_ARN/);
  assert.match(workflow, /REQUIRED_AWS_REGION/);
  assert.match(workflow, /REQUIRED_SERVER_ECR_REPOSITORY/);
  assert.match(workflow, /REQUIRED_AI_ECR_REPOSITORY/);
  assert.match(workflow, /GITHUB_STEP_SUMMARY/);
  assert.match(workflow, /GitHub repository variables/);
  assert.match(workflow, /github\.sha/);
  assert.doesNotMatch(workflow, /:latest/);
  assert.match(workflow, /sbom: true/);
  assert.match(workflow, /provenance: true/);
  assert.match(workflow, /aquasecurity\/trivy-action@/);
  assert.match(workflow, /sigstore\/cosign-installer@/);
  assert.match(workflow, /cosign sign/);
  assert.match(workflow, /Rollback metadata/);
});

test("GitHub templates surface contributor workflow expectations", async () => {
  const pr = await text(".github/pull_request_template.md");
  const issue = await text(".github/ISSUE_TEMPLATE.md");

  assert.match(pr, /task doctor/);
  assert.match(pr, /pnpm ci:local/);
  assert.match(pr, /Dependency changes/);
  assert.match(pr, /UI screenshots/);
  assert.match(pr, /Environment changes/);
  assert.match(issue, /Blocks `task up:dev`/);
  assert.match(issue, /Security issue disclosure question only/);
});

test("server runtime image installs only production server dependencies", async () => {
  const dockerfile = await text("apps/server/Dockerfile");

  assert.match(
    dockerfile,
    /pnpm install --frozen-lockfile --prod --filter server\.\.\./
  );
  assert.doesNotMatch(dockerfile, /pnpm .*deploy/);
  assert.match(dockerfile, /apt-get upgrade -y/);
  assert.match(dockerfile, /COPY packages\/typescript-config packages\/typescript-config/);
  assert.match(dockerfile, /rm -rf[\s\S]*\/root\/\.cache\/node/);
  assert.match(dockerfile, /rm -rf[\s\S]*\/usr\/local\/lib\/node_modules\/npm/);
  assert.match(dockerfile, /rm -rf[\s\S]*\/usr\/local\/lib\/node_modules\/corepack/);
  assert.doesNotMatch(dockerfile, /COPY packages\/typescript-config\/package\.json/);
  assert.doesNotMatch(
    dockerfile,
    /COPY --from=build .*\/app\/node_modules \/app\/node_modules/
  );
});

test("server runtime image lets the non-root user run Prisma migrations", async () => {
  const dockerfile = await text("apps/server/Dockerfile");

  assert.match(dockerfile, /chown -R server:nodejs \/app\/node_modules/);
  assert.match(dockerfile, /USER server/);
  assert.match(dockerfile, /prisma migrate deploy/);
});

test("Dependabot covers npm, GitHub Actions, Dockerfiles, and AI Python requirements", async () => {
  const dependabot = await text(".github/dependabot.yml");

  for (const ecosystem of [
    "npm",
    "github-actions",
    "docker",
    "pip",
  ]) {
    assert.match(dependabot, new RegExp(`package-ecosystem: "${ecosystem}"`));
  }

  assert.match(dependabot, /directory: "\/apps\/server"/);
  assert.match(dependabot, /directory: "\/apps\/ai"/);
});
