import { describe, it, expect } from "vitest";
import path from "node:path";
import os from "node:os";
import fs from "node:fs";
import { parseManifestUseCases, parseManifestFully } from "./_manifest-ast.js";

function writeManifest(content) {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "manifest-ast-"));
  const fp = path.join(dir, "feature.manifest.ts");
  fs.writeFileSync(fp, content);
  return fp;
}

describe("parseManifestUseCases", () => {
  it("returns an empty object for a manifest with no useCases", () => {
    const fp = writeManifest(`export const xManifest = defineFeature({
  name: "x",
  requiredCores: [],
  useCases: {},
  realtimeChannels: [],
  jobs: [],
} as const);`);
    expect(parseManifestUseCases(fp)).toEqual({});
  });

  it("extracts per-use-case publishes/audits/consumes arrays", () => {
    const fp = writeManifest(`export const authManifest = defineFeature({
  name: "auth",
  requiredCores: [],
  useCases: {
    signIn: { mutates: false, audits: [], publishes: [], consumes: [] },
    signUp: { mutates: true, audits: ["user.created"], publishes: ["auth.signed-up"], consumes: [] },
    signOut: { mutates: true, audits: ["session.ended"], publishes: [], consumes: [] },
  },
  realtimeChannels: [],
  jobs: [],
} as const);`);
    expect(parseManifestUseCases(fp)).toEqual({
      signIn: {
        mutates: false,
        audits: [],
        publishes: [],
        consumes: [],
        analyticsEvents: [],
        rateLimit: [],
      },
      signUp: {
        mutates: true,
        audits: ["user.created"],
        publishes: ["auth.signed-up"],
        consumes: [],
        analyticsEvents: [],
        rateLimit: [],
      },
      signOut: {
        mutates: true,
        audits: ["session.ended"],
        publishes: [],
        consumes: [],
        analyticsEvents: [],
        rateLimit: [],
      },
    });
  });

  it("returns null when file does not exist", () => {
    expect(parseManifestUseCases("/nonexistent/manifest.ts")).toBeNull();
  });

  it("returns null when the file has no defineFeature call", () => {
    const fp = writeManifest(`export const x = 1;`);
    expect(parseManifestUseCases(fp)).toBeNull();
  });
});

describe("parseManifestFully", () => {
  it("returns name + requiredCores + useCases for a complete manifest", () => {
    // Use the writeManifest helper that already exists in the test file
    const fp = writeManifest(`export const authManifest = defineFeature({
  name: "auth",
  requiredCores: ["audit", "events"],
  useCases: {
    signIn: { mutates: false, audits: [], publishes: [], consumes: [] },
    signUp: { mutates: true, audits: ["user.created"], publishes: [], consumes: [] },
  },
  realtimeChannels: [],
  jobs: [],
} as const);`);
    expect(parseManifestFully(fp)).toEqual({
      name: "auth",
      requiredCores: ["audit", "events"],
      requiresConsent: [],
      useCases: {
        signIn: {
          mutates: false,
          audits: [],
          publishes: [],
          consumes: [],
          analyticsEvents: [],
          rateLimit: [],
        },
        signUp: {
          mutates: true,
          audits: ["user.created"],
          publishes: [],
          consumes: [],
          analyticsEvents: [],
          rateLimit: [],
        },
      },
    });
  });

  it("extracts requiresConsent categories from the manifest", () => {
    const fp = writeManifest(`export const consentManifest = defineFeature({
  name: "consent",
  requiredCores: [],
  requiresConsent: ["analytics", "marketing"],
  useCases: {},
  realtimeChannels: [],
  jobs: [],
} as const);`);
    expect(parseManifestFully(fp)).toEqual({
      name: "consent",
      requiredCores: [],
      requiresConsent: ["analytics", "marketing"],
      useCases: {},
    });
  });

  it("returns null when the manifest has no name", () => {
    const fp = writeManifest(`export const x = 1;`);
    expect(parseManifestFully(fp)).toBeNull();
  });

  it("is not fooled by 'name:' inside a JSDoc comment (regex would false-match)", () => {
    const fp = writeManifest(`/**
 * Sample comment with name: "fake" embedded in it.
 */
export const realManifest = defineFeature({
  name: "real",
  requiredCores: [],
  useCases: {},
  realtimeChannels: [],
  jobs: [],
} as const);`);
    expect(parseManifestFully(fp)).toEqual({
      name: "real",
      requiredCores: [],
      requiresConsent: [],
      useCases: {},
    });
  });
});
