import type { NonBrowserExecutionContext } from "../../../../schema/src/records/context.js";
import { featureAvailability } from "../../../../core/src/sdk/capability.js";
import type {
  Detector,
  DetectorContext,
  DetectorOutcome,
} from "../../../../core/src/sdk/detector.js";
import { buildBlockingEvidence, type ClosureBlockingEvidence } from "./evidence.js";
import {
  CLOSURE_CAPABILITY,
  detectUni080Violations,
  detectUni081Violations,
  detectUni083Violations,
  isRuleExcepted,
  parseClosureFixture,
  sanitizeForEvidence,
  type ClosureFixture,
  type ClosureViolation,
} from "./types.js";

const DETECTOR_VERSION = "1.0.0";

function capabilityUnavailable(context: DetectorContext): boolean {
  return featureAvailability(context.capabilities, CLOSURE_CAPABILITY) !== "available";
}

function makeExecutionContext(seed: string): NonBrowserExecutionContext {
  return {
    kind: "package" as const,
    surfaceId: "closure-surface",
    adapterId: "closure-surface",
    environment: {},
    seed,
  };
}

function violationsToFacts(violations: ClosureViolation[]) {
  return violations.map((violation) => ({
    scenarioId: violation.scenarioId,
    fact: violation.fact,
    kind: violation.kind,
  }));
}

function buildOutcome(
  detectorId: string,
  fixture: ClosureFixture,
  executionContext: NonBrowserExecutionContext,
  summary: string,
  evidence: ClosureBlockingEvidence,
  violation: unknown,
): DetectorOutcome<ClosureBlockingEvidence> {
  return {
    detector: { id: detectorId, version: DETECTOR_VERSION },
    class: "closure-violation",
    severity: "high",
    target: {
      kind: "closure-fixture",
      canonical: fixture.fixtureId,
    },
    context: executionContext,
    summary,
    evidence: [{ truthSource: "universal", payload: evidence }],
    artifacts: [],
    laneEligibility: "blocking",
    scope: {
      id: `${detectorId}:${fixture.fixtureId}`,
      detectorId,
      surfaceId: executionContext.surfaceId,
    },
    violation,
  };
}

function evaluateViolations(
  detectorId: string,
  rule: string,
  fixture: ClosureFixture,
  context: DetectorContext,
  detect: (scenario: ClosureFixture["scenarios"][number]) => ClosureViolation[],
  summary: string,
): DetectorOutcome<ClosureBlockingEvidence>[] {
  const violations = fixture.scenarios.flatMap((scenario) => detect(scenario));
  if (violations.length === 0) {
    return [];
  }

  const executionContext = makeExecutionContext(context.seed);
  const evidence = buildBlockingEvidence({
    rule,
    positiveWitness: {
      description: `${rule} closure evaluated against fixture scenarios`,
      fixtureId: fixture.fixtureId,
      closureVersion: fixture.closureVersion,
    },
    negativeWitness: {
      description: `${rule} closure violation observed`,
      kind: violations[0]?.kind ?? `${rule.toLowerCase()}-violation`,
      details: sanitizeForEvidence(violations),
    },
    minimalContradictoryFacts: violationsToFacts(violations),
    seed: context.seed,
    surfaceId: executionContext.surfaceId,
    detectorId,
  });

  return [
    buildOutcome(
      detectorId,
      fixture,
      executionContext,
      summary,
      evidence,
      sanitizeForEvidence(violations),
    ),
  ];
}

export const uni080RuntimeReferenceDetector: Detector<unknown, ClosureBlockingEvidence> = {
  id: "uni-080-runtime-reference",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

    const fixture = parseClosureFixture(input);
    if (isRuleExcepted(fixture, "UNI-080")) {
      return Promise.resolve([]);
    }

    return Promise.resolve(
      evaluateViolations(
        "uni-080-runtime-reference",
        "UNI-080",
        fixture,
        context,
        detectUni080Violations,
        "Runtime reference did not resolve in packaged environment",
      ),
    );
  },
};

export const uni081ArtifactClosureDetector: Detector<unknown, ClosureBlockingEvidence> = {
  id: "uni-081-artifact-closure",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

    const fixture = parseClosureFixture(input);
    if (isRuleExcepted(fixture, "UNI-081")) {
      return Promise.resolve([]);
    }

    return Promise.resolve(
      evaluateViolations(
        "uni-081-artifact-closure",
        "UNI-081",
        fixture,
        context,
        detectUni081Violations,
        "Deploy artifact did not derive from current source, config, and lock inputs",
      ),
    );
  },
};

export const uni083InventoryProvenanceDetector: Detector<unknown, ClosureBlockingEvidence> = {
  id: "uni-083-inventory-provenance",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

    const fixture = parseClosureFixture(input);
    if (isRuleExcepted(fixture, "UNI-083")) {
      return Promise.resolve([]);
    }

    return Promise.resolve(
      evaluateViolations(
        "uni-083-inventory-provenance",
        "UNI-083",
        fixture,
        context,
        detectUni083Violations,
        "Discovery inventory lost provenance or admitted a non-production surface",
      ),
    );
  },
};
