import type { Browser } from "playwright";
import {
  classify,
  type ClassifiedRun,
  type DetectorOutcome,
  type KernelStores,
  type HarnessEvent,
} from "../../../core/src/classify/classifier.js";
import { cellFailureEvent } from "../cell-harness.js";
import { asReadonlyKernelStores } from "../../../core/src/sdk/detector.js";
import type { CapabilityProfile } from "../../../core/src/sdk/capability.js";
import type { MatrixCell } from "../../../schema/src/records/context.js";
import type { CaptureOptions, CellEvidence } from "../../../playwright/src/cell-runner.js";
import type { AuthAdapter } from "../../../playwright/src/auth.js";
import type { CoverageGap } from "../../../universal/src/oracle/witnesses.js";
import type { Detector } from "../../../core/src/sdk/detector.js";
import type { RedactionRule } from "../../../playwright/src/redaction.js";
import { runRelationsCell } from "./capture.js";
import { cellScreenshotArtifacts } from "../../../playwright/src/cell-original.js";
import type { RelationsCellEvidence, Ui001EvidenceRecord } from "./types.js";

export type RelationsMatrixPlan = {
  cells: MatrixCell[];
  coverageGaps?: CoverageGap[];
};

export type RunRelationsMatrixInput = {
  plan: RelationsMatrixPlan;
  baseUrl: string;
  detectors: Detector<RelationsCellEvidence, Ui001EvidenceRecord>[];
  stores: KernelStores;
  runId: string;
  seed: string;
  clockStart: string;
  browser: Browser;
  capture: CaptureOptions;
  timeouts: { navigateMs: number; settleMs: number };
  capabilities: CapabilityProfile;
  adapters?: Record<string, AuthAdapter>;
  artifactRunDir?: string;
  redactionRules?: RedactionRule[];
  onCellEvidence?: (evidence: CellEvidence) => void;
};

function browserExecutionContext(cell: MatrixCell) {
  return { kind: "browser" as const, cell: { id: cell.id } };
}

function detectorScope(cell: MatrixCell, detectorId: string) {
  return {
    id: cell.id,
    detectorId,
    surfaceId: cell.id,
  };
}

function toClassifierOutcome(
  outcome: Awaited<
    ReturnType<Detector<RelationsCellEvidence, Ui001EvidenceRecord>["evaluate"]>
  >[number],
  cell: MatrixCell,
  cellArtifacts: DetectorOutcome["artifacts"],
): DetectorOutcome {
  return {
    detector: outcome.detector,
    class: outcome.class,
    severity: outcome.severity,
    target: outcome.target,
    context: browserExecutionContext(cell),
    summary: outcome.summary,
    evidence: outcome.evidence,
    artifacts: [...outcome.artifacts, ...cellArtifacts],
    laneEligibility: outcome.laneEligibility,
    scope: detectorScope(cell, outcome.detector.id),
    violation: outcome.violation,
    ...(outcome.proofConditionMet === undefined
      ? {}
      : { proofConditionMet: outcome.proofConditionMet }),
    ...(outcome.contextDimensions === undefined
      ? {}
      : { contextDimensions: outcome.contextDimensions }),
  };
}

export async function runRelationsMatrix(
  input: RunRelationsMatrixInput,
): Promise<ClassifiedRun> {
  let clockTick = 0;
  const clock = (): string => new Date(Date.parse(input.clockStart) + clockTick++).toISOString();
  const detectorContext = {
    clock,
    seed: input.seed,
    capabilities: input.capabilities,
    readStores: asReadonlyKernelStores(input.stores),
  };

  const sortedCells = [...input.plan.cells].sort((left, right) =>
    left.id.localeCompare(right.id),
  );
  const sortedDetectors = [...input.detectors].sort((left, right) =>
    left.id.localeCompare(right.id),
  );

  const detectorOutcomes: DetectorOutcome[] = [];
  const harnessEvents: HarnessEvent[] = [];

  for (const cell of sortedCells) {
    const adapter = input.adapters?.[cell.role];
    let result: Awaited<ReturnType<typeof runRelationsCell>>;
    try {
      result = await runRelationsCell({
        cell,
        baseUrl: input.baseUrl,
        browser: input.browser,
        seed: input.seed,
        clockStart: input.clockStart,
        capture: input.capture,
        timeouts: input.timeouts,
        ...(adapter === undefined ? {} : { authAdapter: adapter }),
        ...(input.artifactRunDir === undefined ? {} : { artifactRunDir: input.artifactRunDir }),
        sourceRunId: input.runId,
        ...(input.redactionRules === undefined ? {} : { redactionRules: input.redactionRules }),
      });
    } catch (error) {
      harnessEvents.push(cellFailureEvent(cell, error));
      continue;
    }

    if (result.kind !== "evidence") {
      harnessEvents.push(result.outcome);
      continue;
    }

    input.onCellEvidence?.(result.evidence);

    const cellArtifacts = cellScreenshotArtifacts(result.evidence);
    for (const detector of sortedDetectors) {
      const outcomes = await detector.evaluate(result.evidence, detectorContext);
      for (const outcome of outcomes) {
        detectorOutcomes.push(toClassifierOutcome(outcome, cell, cellArtifacts));
      }
    }
  }

  return classify({
    detectorOutcomes,
    harnessEvents,
    coverageEvents: [],
    stores: input.stores,
    runId: input.runId,
  });
}
