import type {
  Detector,
  DetectorContext,
  DetectorOutcome,
} from "../../../core/src/sdk/detector.js";
import { isIntentionallyExcepted } from "./contracts.js";
import {
  detectCanceledNestedViolations,
  detectFailedFormContinuityViolations,
  detectHiddenInertViolations,
  detectItemIdentityViolations,
  detectVisualOnlyProofViolations,
} from "./detect.js";
import { buildUi001Evidence } from "./evidence.js";
import type {
  InteractionCellEvidence,
  InteractionRuleId,
  InteractionViolationFact,
  Ui001EvidenceRecord,
} from "./types.js";
import { INTERACTION_DETECTOR_VERSION } from "./types.js";

type InteractionDetectorConfig = {
  id: string;
  className: string;
  ruleId: InteractionRuleId;
  detect: (evidence: InteractionCellEvidence) => InteractionViolationFact[];
  classificationBasis: (violation: InteractionViolationFact) => string;
};

function browserContext(cellId: string) {
  return { kind: "browser" as const, cell: { id: cellId } };
}

function buildOutcome(
  config: InteractionDetectorConfig,
  evidence: InteractionCellEvidence,
  violation: InteractionViolationFact,
  context: DetectorContext,
): DetectorOutcome<Ui001EvidenceRecord> {
  const ui001 = buildUi001Evidence(
    evidence,
    config.ruleId,
    violation,
    config.classificationBasis(violation),
  );

  void context;
  return {
    detector: { id: config.id, version: INTERACTION_DETECTOR_VERSION },
    class: config.className,
    severity: "high",
    target: { kind: "dom", canonical: violation.locator },
    context: browserContext(evidence.cell.id),
    summary: violation.summary,
    evidence: [{ truthSource: "universal", payload: ui001 }],
    artifacts: [],
    laneEligibility: "blocking",
    scope: {
      id: `${config.id}:${evidence.cell.id}:${violation.locator}:${violation.kind}`,
      detectorId: config.id,
      surfaceId: evidence.cell.id,
    },
    violation,
    contextDimensions: {
      route: evidence.cell.routeId,
      role: evidence.cell.role,
      locale: evidence.cell.locale,
    },
  };
}

function evaluateInteractionDetector(
  config: InteractionDetectorConfig,
  evidence: InteractionCellEvidence,
  context: DetectorContext,
): DetectorOutcome<Ui001EvidenceRecord>[] {
  if (isIntentionallyExcepted(context, evidence.cell, config.id, config.ruleId)) {
    return [];
  }

  const violations = config.detect(evidence);
  if (violations.length === 0) {
    return [];
  }

  return violations.map((violation) => buildOutcome(config, evidence, violation, context));
}

function makeDetector(
  config: InteractionDetectorConfig,
): Detector<InteractionCellEvidence, Ui001EvidenceRecord> {
  return {
    id: config.id,
    version: INTERACTION_DETECTOR_VERSION,
    defaultLane: "blocking",
    evaluate(evidence, context) {
      return Promise.resolve(evaluateInteractionDetector(config, evidence, context));
    },
  };
}

export const interactionPromiseDetector = makeDetector({
  id: "ui-033-interaction-promise",
  className: "interaction-visual-only-proof",
  ruleId: "UI-033",
  detect: detectVisualOnlyProofViolations,
  classificationBasis: () =>
    "visual state changed without corroborating accessible, DOM, URL, storage, network, or side-effect evidence",
});

export const failedFormContinuityDetector = makeDetector({
  id: "ui-035-failed-form-continuity",
  className: "failed-form-input-continuity",
  ruleId: "UI-035",
  detect: detectFailedFormContinuityViolations,
  classificationBasis: (violation) =>
    `failed form action violated ${violation.uniRule ?? "UNI-090"} input continuity`,
});

export const canceledNestedDetector = makeDetector({
  id: "ui-036-canceled-nested-interaction",
  className: "canceled-nested-side-effect",
  ruleId: "UI-036",
  detect: detectCanceledNestedViolations,
  classificationBasis: (violation) =>
    `canceled nested interaction violated ${violation.uniRule ?? "UNI-091"} side-effect absence`,
});

export const itemIdentityContinuityDetector = makeDetector({
  id: "ui-037-item-identity-continuity",
  className: "item-identity-continuity",
  ruleId: "UI-037",
  detect: detectItemIdentityViolations,
  classificationBasis: (violation) =>
    `reorder or reconciliation violated ${violation.uniRule ?? "UNI-092"} item-local state continuity`,
});

export const hiddenInertActionDetector = makeDetector({
  id: "ui-038-hidden-inert-action",
  className: "hidden-inert-action",
  ruleId: "UI-038",
  detect: detectHiddenInertViolations,
  classificationBasis: (violation) =>
    `hidden or inert control violated ${violation.uniRule ?? "UNI-093"} focus, hit, accessibility, or side-effect absence`,
});

export const interactionDetectors = [
  interactionPromiseDetector,
  failedFormContinuityDetector,
  canceledNestedDetector,
  itemIdentityContinuityDetector,
  hiddenInertActionDetector,
] as const;

export const interactionDetectorByRuleId: Record<
  InteractionRuleId,
  Detector<InteractionCellEvidence, Ui001EvidenceRecord>
> = {
  "UI-033": interactionPromiseDetector,
  "UI-035": failedFormContinuityDetector,
  "UI-036": canceledNestedDetector,
  "UI-037": itemIdentityContinuityDetector,
  "UI-038": hiddenInertActionDetector,
};
