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 ProtocolBlockingEvidence } from "./evidence.js";
import {
  PROTOCOL_CAPABILITY,
  detectUni060Violations,
  detectUni061Violations,
  detectUni062Violations,
  isRuleExcepted,
  parseProtocolFixture,
  sanitizeForEvidence,
  type ProtocolFixture,
  type ProtocolViolation,
} from "./types.js";

const DETECTOR_VERSION = "1.0.0";

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

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

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

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

function evaluateViolations(
  detectorId: string,
  rule: string,
  fixture: ProtocolFixture,
  context: DetectorContext,
  detect: (scenario: ProtocolFixture["scenarios"][number]) => ProtocolViolation[],
  summary: string,
  laneEligibility: "blocking" | "blocking-eligible",
): DetectorOutcome<ProtocolBlockingEvidence>[] {
  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} protocol integrity evaluated against fixture scenarios`,
      fixtureId: fixture.fixtureId,
      protocolVersion: fixture.protocolVersion,
    },
    negativeWitness: {
      description: `${rule} protocol integrity 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,
      laneEligibility,
    ),
  ];
}

export const uni060StreamIntegrityDetector: Detector<unknown, ProtocolBlockingEvidence> = {
  id: "uni-060-stream-integrity",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

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

    return Promise.resolve(
      evaluateViolations(
        "uni-060-stream-integrity",
        "UNI-060",
        fixture,
        context,
        detectUni060Violations,
        "Stream protocol violated framing, order, terminal-record, or chunk-boundary independence",
        "blocking",
      ),
    );
  },
};

export const uni061StdoutPurityDetector: Detector<unknown, ProtocolBlockingEvidence> = {
  id: "uni-061-stdout-purity",
  version: DETECTOR_VERSION,
  defaultLane: "blocking-eligible",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

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

    return Promise.resolve(
      evaluateViolations(
        "uni-061-stdout-purity",
        "UNI-061",
        fixture,
        context,
        detectUni061Violations,
        "Machine-readable stdout contained contamination or terminal integrity defects",
        "blocking-eligible",
      ),
    );
  },
};

export const uni062RoundTripDetector: Detector<unknown, ProtocolBlockingEvidence> = {
  id: "uni-062-round-trip",
  version: DETECTOR_VERSION,
  defaultLane: "blocking",
  evaluate(input, context) {
    if (capabilityUnavailable(context)) {
      return Promise.resolve([]);
    }

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

    return Promise.resolve(
      evaluateViolations(
        "uni-062-round-trip",
        "UNI-062",
        fixture,
        context,
        detectUni062Violations,
        "Serialization round-trip violated explicit compatibility contract",
        "blocking",
      ),
    );
  },
};
