import {
  detectUni090Violations,
  detectUni091Violations,
  sanitizeForEvidence,
  type ContinuityViolation,
} from "../../../universal/src/primitives/continuity/types.js";
import type { InteractionScenario } from "./types.js";

export type InteractionContinuityViolation = ContinuityViolation & {
  uniRule: "UNI-090" | "UNI-091" | "UNI-092" | "UNI-093";
};

function valuesEqual(left: unknown, right: unknown): boolean {
  return JSON.stringify(left) === JSON.stringify(right);
}

export function detectUni092Violations(
  scenario: InteractionScenario,
): InteractionContinuityViolation[] {
  if (scenario.kind !== "item-identity") {
    return [];
  }

  const violations: InteractionContinuityViolation[] = [];
  for (const item of scenario.items) {
    const observed = scenario.observedAfterOperation.find(
      (entry) => entry.itemId === item.itemId,
    );
    if (observed === undefined) {
      violations.push({
        scenarioId: scenario.scenarioId,
        kind: "item-state-missing",
        fact: "item-local state missing after identity-preserving operation",
        uniRule: "UNI-092",
        details: sanitizeForEvidence({
          itemId: item.itemId,
          expectedLocalState: item.localState,
          operation: scenario.operation,
        }),
      });
      continue;
    }

    if (!valuesEqual(item.localState, observed.localState)) {
      violations.push({
        scenarioId: scenario.scenarioId,
        kind: "item-state-not-preserved",
        fact: "item-local state did not follow stable item identity",
        uniRule: "UNI-092",
        details: sanitizeForEvidence({
          itemId: item.itemId,
          expectedLocalState: item.localState,
          observedLocalState: observed.localState,
          operation: scenario.operation,
        }),
      });
    }
  }

  return violations;
}

export function detectUni093Violations(
  scenario: InteractionScenario,
): InteractionContinuityViolation[] {
  if (scenario.kind !== "hidden-inert-action") {
    return [];
  }

  const violations: InteractionContinuityViolation[] = [];
  const checks: Array<{ kind: string; fact: string; active: boolean }> = [
    {
      kind: "focusable-while-inert",
      fact: "hidden or inert control remains focusable",
      active: scenario.focusable,
    },
    {
      kind: "clickable-while-inert",
      fact: "hidden or inert control remains clickable via hit testing",
      active: scenario.hitTestActivates,
    },
    {
      kind: "announced-actionable-while-inert",
      fact: "hidden or inert control remains announced as actionable",
      active: scenario.announcedActionable,
    },
    {
      kind: "keyboard-activates-while-inert",
      fact: "hidden or inert control remains keyboard activatable",
      active: scenario.keyboardActivates,
    },
    {
      kind: "side-effect-while-inert",
      fact: "hidden or inert control remains side-effecting",
      active: scenario.sideEffectEmitted,
    },
  ];

  for (const check of checks) {
    if (!check.active) {
      continue;
    }
    violations.push({
      scenarioId: scenario.scenarioId,
      kind: check.kind,
      fact: check.fact,
      uniRule: "UNI-093",
      details: sanitizeForEvidence({
        locator: scenario.locator,
        inertReason: scenario.inertReason,
      }),
    });
  }

  return violations;
}

export function detectContinuityViolations(
  scenario: InteractionScenario,
): InteractionContinuityViolation[] {
  switch (scenario.kind) {
    case "form-submission":
      return detectUni090Violations(scenario).map((violation) => ({
        ...violation,
        uniRule: "UNI-090" as const,
      }));
    case "canceled-interaction":
      return detectUni091Violations(scenario).map((violation) => ({
        ...violation,
        uniRule: "UNI-091" as const,
      }));
    case "item-identity":
      return detectUni092Violations(scenario);
    case "hidden-inert-action":
      return detectUni093Violations(scenario);
    default:
      return [];
  }
}
