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 IsolationBlockingEvidence } from "./evidence.js";
import {
  detectUni070Violations,
  detectUni073Violations,
  ISOLATION_CAPABILITY,
  isRuleExcepted,
  parseIsolationFixture,
  sanitizeForEvidence,
  type IsolationFixture,
  type IsolationViolation,
} from "./types.js";

const DETECTOR_VERSION = "1.0.0";

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

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

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

function buildOutcome(
  detectorId: string,
  fixture: IsolationFixture,
  executionContext: NonBrowserExecutionContext,
  summary: string,
  evidence: IsolationBlockingEvidence,
  violation: unknown,
): DetectorOutcome<IsolationBlockingEvidence> {
  return {
    detector: { id: detectorId, version: DETECTOR_VERSION },
    class: "isolation-violation",
    severity: "high",
    target: {
      kind: "isolation-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: IsolationFixture,
  context: DetectorContext,
  detect: (scenario: IsolationFixture["scenarios"][number]) => IsolationViolation[],
  summary: string,
): DetectorOutcome<IsolationBlockingEvidence>[] {
  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} isolation evaluated against fixture scenarios`,
      fixtureId: fixture.fixtureId,
      isolationVersion: fixture.isolationVersion,
    },
    negativeWitness: {
      description: `${rule} isolation 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,
      violations,
    ),
  ];
}

export const uni070MetamorphicIsolationDetector: Detector<unknown, IsolationBlockingEvidence> = {
  id: "uni-070-metamorphic-isolation",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

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

    return Promise.resolve(
      evaluateViolations(
        "uni-070-metamorphic-isolation",
        "UNI-070",
        fixture,
        context,
        detectUni070Violations,
        "Independent scenario result was not invariant under order, repetition, or parallel execution",
      ),
    );
  },
};

export const uni073ModeIsolationDetector: Detector<unknown, IsolationBlockingEvidence> = {
  id: "uni-073-mode-isolation",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

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

    return Promise.resolve(
      evaluateViolations(
        "uni-073-mode-isolation",
        "UNI-073",
        fixture,
        context,
        detectUni073Violations,
        "Declared-isolation mode reached a live or production dependency",
      ),
    );
  },
};
