import { describe, it } from "vitest";
import { RuleTester } from "eslint";
import path from "node:path";
import os from "node:os";
import fs from "node:fs";
import rule from "./no-undeclared-consent-check.js";

function makeFixture({ requiresConsent, useCaseBody }) {
  const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nucc-"));
  const featureDir = path.join(repoRoot, "packages", "demo");
  fs.mkdirSync(path.join(featureDir, "src", "application", "use-cases"), {
    recursive: true,
  });
  const consentField =
    requiresConsent.length > 0
      ? `  requiresConsent: [${requiresConsent.map((c) => `"${c}"`).join(", ")}],\n`
      : "";
  fs.writeFileSync(
    path.join(featureDir, "src", "feature.manifest.ts"),
    `export const demoManifest = defineFeature({
  name: "demo",
  requiredCores: [],
${consentField}  useCases: {
    checkData: { mutates: false, audits: [], publishes: [], consumes: [] },
  },
  realtimeChannels: [],
  jobs: [],
} as const);`,
  );
  const useCaseFile = path.join(
    featureDir,
    "src",
    "application",
    "use-cases",
    "check-data.use-case.ts",
  );
  fs.writeFileSync(useCaseFile, useCaseBody);
  return { repoRoot, useCaseFile };
}

const tester = new RuleTester({
  languageOptions: {
    parser: await import("@typescript-eslint/parser"),
    ecmaVersion: "latest",
    sourceType: "module",
  },
});

describe("no-undeclared-consent-check", () => {
  it("passes when consent.isGranted category matches manifest requiresConsent", () => {
    const { repoRoot, useCaseFile } = makeFixture({
      requiresConsent: ["analytics"],
      useCaseBody: `export const checkDataUseCase = (consent) => async () => { if (!consent.isGranted("analytics")) throw new Error(); };`,
    });
    tester.run("no-undeclared-consent-check", rule, {
      valid: [
        {
          filename: useCaseFile,
          code: fs.readFileSync(useCaseFile, "utf8"),
          options: [{ repoRoot }],
        },
      ],
      invalid: [],
    });
  });

  it("fires when category is undeclared or manifest declaration is unused", () => {
    const { repoRoot: rootA, useCaseFile: fileA } = makeFixture({
      requiresConsent: ["analytics"],
      useCaseBody: `export const checkDataUseCase = (consent) => async () => { if (!consent.isGranted("marketing")) throw new Error(); };`,
    });
    const { repoRoot: rootB, useCaseFile: fileB } = makeFixture({
      requiresConsent: ["analytics"],
      useCaseBody: `export const checkDataUseCase = () => async () => { return { ok: true }; };`,
    });
    tester.run("no-undeclared-consent-check", rule, {
      valid: [],
      invalid: [
        {
          filename: fileA,
          code: fs.readFileSync(fileA, "utf8"),
          options: [{ repoRoot: rootA }],
          errors: [
            {
              messageId: "undeclared",
              data: { category: "marketing", useCase: "checkData" },
            },
          ],
        },
        {
          filename: fileB,
          code: fs.readFileSync(fileB, "utf8"),
          options: [{ repoRoot: rootB }],
          errors: [
            { messageId: "unusedDeclaration", data: { useCase: "checkData" } },
          ],
        },
      ],
    });
  });

  it("is a no-op for non-use-case files", () => {
    const { repoRoot } = makeFixture({
      requiresConsent: ["analytics"],
      useCaseBody: `export const checkDataUseCase = (consent) => async () => { consent.isGranted("unknown"); };`,
    });
    const serviceFile = path.join(
      repoRoot,
      "packages",
      "demo",
      "src",
      "application",
      "services",
      "data.service.ts",
    );
    fs.mkdirSync(path.dirname(serviceFile), { recursive: true });
    fs.writeFileSync(
      serviceFile,
      `export function doWork(consent) { consent.isGranted("unknown"); }`,
    );
    tester.run("no-undeclared-consent-check", rule, {
      valid: [
        {
          filename: serviceFile,
          code: fs.readFileSync(serviceFile, "utf8"),
          options: [{ repoRoot }],
        },
      ],
      invalid: [],
    });
  });
});
