import { basename } from "node:path";
import { classify, type DetectorOutcome } from "../../core/src/classify/classifier.js";
import { createReadonlyStores } from "../../cli/src/commands/verify.js";
import { createNamedSchedule, runSchedule } from "../../feature/src/adapters/concurrency/index.js";
import { evaluateObligationClosure, runQueueBranch } from "../../feature/src/async/index.js";
import { runJourneyBranch } from "../../feature/src/journey/runner.js";
import type { Finding } from "../../schema/src/records/finding.js";
import { apiStatefulJourney, apiStatefulSystems, brokenPersistenceJourney, cleanPersistenceJourney } from "../fixtures/api-stateful/fixture.js";
import { asyncQueueSystems } from "../fixtures/async-queue/fixture.js";
import { readManifest, scoreFindings, type CorpusScore } from "./harness.js";

export type CorpusApiAsyncFixtureResult = {
  findings: Finding[];
  cleanFindings: Finding[];
  score: CorpusScore;
};

function universalOutcome(input: {
  id: string;
  detectorId: string;
  detectorClass: string;
  targetKind: string;
  adapterId: string;
  error: string;
  seed?: string;
}): DetectorOutcome {
  return {
    detector: { id: input.detectorId, version: "1.0.0" },
    class: input.detectorClass,
    severity: "high",
    target: { kind: input.targetKind, canonical: input.id },
    context: { kind: "api", surfaceId: input.id, adapterId: input.adapterId, environment: {}, seed: input.seed ?? input.id },
    summary: `${input.detectorClass} violated its atomicity oracle for ${input.id}`,
    evidence: [{ truthSource: "universal", payload: { id: input.id, error: input.error } }],
    artifacts: [],
    laneEligibility: "blocking-eligible",
    proofConditionMet: true,
    scope: { id: `${input.detectorId}:${input.id}`, detectorId: input.detectorId, surfaceId: input.id },
    violation: { id: input.id, error: input.error },
    contextDimensions: { detector: input.detectorId },
  };
}

async function classifyUniversalFailure(input: Parameters<typeof universalOutcome>[0]): Promise<Finding[]> {
  const { stores } = await createReadonlyStores("2026-07-25T12:00:00.000Z");
  const result = classify({
    detectorOutcomes: [universalOutcome(input)],
    harnessEvents: [],
    coverageEvents: [],
    stores,
    runId: `corpus-${input.id}`,
  });
  return result.findings;
}

async function runApiStatefulFixture(): Promise<{ findings: Finding[]; cleanFindings: Finding[] }> {
  const brokenJourney = await runJourneyBranch(apiStatefulJourney, {
    ...brokenPersistenceJourney(),
    runId: "corpus-api-stateful-persistence",
  });
  const cleanJourney = await runJourneyBranch(apiStatefulJourney, {
    ...cleanPersistenceJourney(),
    runId: "corpus-api-stateful-clean-persistence",
  });
  const duplicateId = "api-stateful.duplicate-retry";
  const lostUpdateId = "api-stateful.lost-update";
  const [brokenDuplicate, brokenLostUpdate, cleanDuplicate, cleanLostUpdate] = await Promise.all([
    runSchedule(createNamedSchedule("retry-overlap", { id: duplicateId }), apiStatefulSystems.brokenDuplicateRetrySystem),
    runSchedule(createNamedSchedule("lost-update", { id: lostUpdateId }), apiStatefulSystems.brokenLostUpdateSystem),
    runSchedule(createNamedSchedule("retry-overlap", { id: "api-stateful.clean-duplicate-retry" }), apiStatefulSystems.goodDuplicateRetrySystem),
    runSchedule(createNamedSchedule("lost-update", { id: "api-stateful.clean-lost-update" }), apiStatefulSystems.goodLostUpdateSystem),
  ]);
  if (brokenDuplicate.holds || brokenLostUpdate.holds || !cleanDuplicate.holds || !cleanLostUpdate.holds) {
    throw new Error("stateful API fixture did not preserve planted and clean schedule controls");
  }
  const scheduleFindings = await Promise.all([
    classifyUniversalFailure({ id: duplicateId, detectorId: "deterministic-schedule", detectorClass: "async-obligation-violation", targetKind: "concurrency-schedule", adapterId: "deterministic-schedule", error: brokenDuplicate.error ?? "duplicate retry violated atomicity" }),
    classifyUniversalFailure({ id: lostUpdateId, detectorId: "deterministic-schedule", detectorClass: "async-obligation-violation", targetKind: "concurrency-schedule", adapterId: "deterministic-schedule", error: brokenLostUpdate.error ?? "lost update violated atomicity" }),
  ]);
  return {
    findings: [...brokenJourney.classified.findings, ...scheduleFindings.flat()],
    cleanFindings: cleanJourney.classified.findings,
  };
}

async function runAsyncQueueFixture(): Promise<{ findings: Finding[]; cleanFindings: Finding[] }> {
  const [brokenPremature, cleanPremature] = await Promise.all([
    asyncQueueSystems.brokenPrematureSuccessSystem.runJourney(),
    asyncQueueSystems.goodObligationClosureSystem.runJourney(),
  ]);
  const [premature, cleanClosure, poison, replay, cleanQueue] = await Promise.all([
    evaluateObligationClosure({
      branchId: "async-queue.premature-success", claimedStatus: "success", obligations: brokenPremature.obligations,
      durableOutcome: brokenPremature.durableOutcome, observableSideEffect: brokenPremature.observableSideEffect,
    }, { runId: "corpus-async-queue-premature" }),
    evaluateObligationClosure({
      branchId: "async-queue.clean-success", claimedStatus: "success", obligations: cleanPremature.obligations,
      durableOutcome: cleanPremature.durableOutcome, observableSideEffect: cleanPremature.observableSideEffect,
    }, { runId: "corpus-async-queue-clean" }),
    runQueueBranch({ branchId: "async-queue.worker", ownedScenarios: ["poison-message"] }, asyncQueueSystems.brokenPoisonQueueSystem, { runId: "corpus-async-queue-poison" }),
    runQueueBranch({ branchId: "async-queue.worker", ownedScenarios: ["duplicate"] }, asyncQueueSystems.brokenDuplicateQueueSystem, { runId: "corpus-async-queue-replay" }),
    runQueueBranch({ branchId: "async-queue.clean-worker", ownedScenarios: ["retry", "duplicate", "poison-message", "dead-letter"] }, asyncQueueSystems.goodQueueBranchSystem, { runId: "corpus-async-queue-clean-worker" }),
  ]);
  if (premature.holds || poison.holds || replay.holds || !cleanClosure.holds || !cleanQueue.holds) {
    throw new Error("async queue fixture did not preserve planted and clean controls");
  }
  return {
    findings: [
      ...premature.classified.findings,
      ...(await classifyUniversalFailure({ id: "async-queue.worker", detectorId: "async-queue-branch", detectorClass: "async-queue-branch-violation", targetKind: "journey-branch", adapterId: "async-queue-branch", seed: "async-queue.worker:poison-message", error: poison.scenarioResults[0]?.error ?? "poison message handling violated dead-letter contract" })),
      ...(await classifyUniversalFailure({ id: "async-queue.worker", detectorId: "async-queue-branch", detectorClass: "async-queue-branch-violation", targetKind: "journey-branch", adapterId: "async-queue-branch", seed: "async-queue.worker:duplicate", error: replay.scenarioResults[0]?.error ?? "webhook replay violated idempotency" })),
    ],
    cleanFindings: [...cleanClosure.classified.findings, ...cleanQueue.classified.findings],
  };
}

export async function runCorpusApiAsyncFixture(fixtureDir: string): Promise<CorpusApiAsyncFixtureResult> {
  const fixture = basename(fixtureDir);
  const result = fixture === "api-stateful"
    ? await runApiStatefulFixture()
    : fixture === "async-queue"
      ? await runAsyncQueueFixture()
      : undefined;
  if (result === undefined) {
    throw new Error(`unknown API/async corpus fixture: ${fixture}`);
  }
  return { ...result, score: scoreFindings(await readManifest(fixtureDir), result.findings) };
}
