import { basename } from "node:path";
import type { Finding } from "../../schema/src/records/finding.js";
import {
  checkProvenanceAuthority,
  runNegativeSecurityJourney,
  type NegativeSecurityJourneySpec,
  type ProvenanceAuthoritySpec,
  type SecurityBoundarySystem,
} from "../../feature/src/adapters/security/index.js";
import { authRolesFixture } from "../fixtures/auth-roles/fixture.js";
import { tenantFixture } from "../fixtures/tenant/fixture.js";
import { readManifest, scoreFindings, type CorpusScore } from "./harness.js";

type NegativeScenario = {
  kind: "negative";
  system: SecurityBoundarySystem;
  spec: NegativeSecurityJourneySpec;
};

type ProvenanceScenario = {
  kind: "provenance";
  spec: ProvenanceAuthoritySpec;
};

type SecurityScenario = NegativeScenario | ProvenanceScenario;

export type SecurityCorpusFixture = {
  id: "auth-roles" | "tenant";
  requiredRoles: string[];
  exercisedRoles: string[];
  authFixture?: NegativeScenario;
  scenarios: SecurityScenario[];
};

export type CorpusSecurityFixtureResult = {
  coverageGaps: string[];
  findings: Finding[];
  score: CorpusScore;
};

function fixtureForDirectory(fixtureDir: string): SecurityCorpusFixture {
  switch (basename(fixtureDir)) {
    case "auth-roles":
      return authRolesFixture;
    case "tenant":
      return tenantFixture;
    default:
      throw new Error(`unknown security corpus fixture: ${fixtureDir}`);
  }
}

function hasRoleMatrixGap(fixture: SecurityCorpusFixture): boolean {
  return fixture.requiredRoles.some((role) => !fixture.exercisedRoles.includes(role));
}

export async function runCorpusSecurityFixture(
  fixtureDir: string,
): Promise<CorpusSecurityFixtureResult> {
  const fixture = fixtureForDirectory(fixtureDir);
  const coverageGaps: string[] = [];
  const findings: Finding[] = [];

  if (fixture.authFixture !== undefined) {
    const authResult = await runNegativeSecurityJourney(
      fixture.authFixture.system,
      fixture.authFixture.spec,
      `corpus-${fixture.id}-auth-fixture`,
    );
    if (authResult.coverageGap !== undefined) {
      coverageGaps.push(authResult.coverageGap.reason);
    }
  }

  if (hasRoleMatrixGap(fixture)) {
    coverageGaps.push("role-matrix-gap");
  }

  for (const scenario of fixture.scenarios) {
    if (scenario.kind === "negative") {
      const result = await runNegativeSecurityJourney(
        scenario.system,
        scenario.spec,
        `corpus-${fixture.id}-${scenario.spec.id}`,
      );
      findings.push(...result.classified.findings);
      continue;
    }

    const result = await checkProvenanceAuthority(
      scenario.spec,
      `corpus-${fixture.id}-${scenario.spec.id}`,
    );
    findings.push(...result.classified.findings);
  }

  return {
    coverageGaps,
    findings,
    score: scoreFindings(await readManifest(fixtureDir), findings),
  };
}
