import { createHash } from "node:crypto";

type NativeStatus = "queued" | "running" | "completed" | "failed" | "blocked" | "canceled";

interface NativeStep {
  status: NativeStatus;
  stage: string;
  terminal: boolean;
  blocker?: { code: string; summary: string; layer: string };
}

interface NativeRunState {
  runId: string;
  caseId: string;
  cursor: number;
  steps: NativeStep[];
  createdAt: string;
}

const MODE = "native-test";
const CONTRACT_VERSION = "caserun.v1";
const BASE_TIME = Date.parse("2026-07-17T00:00:00.000Z");

const scenarios: Record<string, { title: string; steps: NativeStep[] }> = {
  "native-queued": { title: "原生排队中", steps: [{ status: "queued", stage: "queued", terminal: false }] },
  "native-running": { title: "原生运行中", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "agent-running", terminal: false }] },
  "native-completed": { title: "原生已完成", steps: [
    { status: "queued", stage: "queued", terminal: false },
    { status: "running", stage: "prepare", terminal: false },
    { status: "running", stage: "prepared", terminal: false },
    { status: "running", stage: "build-completed", terminal: false },
    { status: "running", stage: "collect-completed", terminal: false },
    { status: "completed", stage: "completed", terminal: true }
  ] },
  "native-failed": { title: "原生失败", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "building", terminal: false }, { status: "failed", stage: "failed", terminal: true, blocker: { code: "native-build-failed", summary: "原生构建按确定性场景失败", layer: "build" } }] },
  "native-blocked": { title: "原生受阻", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "blocked", stage: "preparing", terminal: true, blocker: { code: "native-capability-blocked", summary: "原生能力按确定性场景受阻", layer: "hwpod" } }] },
  "native-canceled": { title: "原生已取消", steps: [{ status: "queued", stage: "queued", terminal: false }, { status: "running", stage: "agent-running", terminal: false }, { status: "canceled", stage: "canceled", terminal: true }] }
};

let runSequence = 0;
const runs = new Map<string, NativeRunState>();

function timestamp(index: number): string {
  return new Date(BASE_TIME + index * 1000).toISOString();
}

function aggregateResponse(run: NativeRunState) {
  const step = run.steps[run.cursor] ?? run.steps.at(-1)!;
  const artifact = {
    path: "native-result.json",
    bytes: 335,
    sha256: createHash("sha256").update(`${run.runId}:native-result.json`).digest("hex")
  };
  const summary = {
    mode: MODE,
    status: step.status,
    jobId: `job-${run.runId}`,
    artifacts: step.status === "completed" ? [artifact] : [],
    artifactCount: step.status === "completed" ? 1 : 0
  };
  const evidence = {
    mode: MODE,
    status: step.status,
    blocker: step.blocker ?? null,
    artifacts: step.status === "completed" ? [artifact] : []
  };
  const archivedRun = {
    runId: run.runId,
    caseId: run.caseId,
    status: step.status,
    stage: step.stage,
    terminal: step.terminal,
    mode: MODE,
    hardwareRequired: false
  };
  const source = { runId: run.runId, caseId: run.caseId, status: step.status, summary, evidence, run: archivedRun };
  return {
    ok: true,
    mode: MODE,
    contractVersion: "caserun.aggregate.v1",
    runId: run.runId,
    caseId: run.caseId,
    status: step.status,
    stage: step.stage,
    terminal: step.terminal,
    sourceAuthority: { mode: MODE, sequence: "request-driven" },
    sha256: createHash("sha256").update(JSON.stringify(source)).digest("hex"),
    result: { run: archivedRun, summary, evidence },
    summary,
    evidence,
    run: archivedRun
  };
}

function runResponse(run: NativeRunState) {
  const step = run.steps[run.cursor] ?? run.steps.at(-1)!;
  return {
    ok: true,
    mode: MODE,
    contractVersion: CONTRACT_VERSION,
    runId: run.runId,
    caseId: run.caseId,
    workflowId: `harnessrl-caserun-${run.runId}`,
    workflowRunId: `native-workflow-${run.runId}`,
    status: step.status,
    stage: step.stage,
    terminal: step.terminal,
    blocker: step.blocker ?? null,
    createdAt: run.createdAt,
    updatedAt: new Date(Date.parse(run.createdAt) + run.cursor * 1000).toISOString(),
    facts: {
      jobId: `job-${run.runId}`,
      artifactCount: step.status === "completed" ? 1 : 0,
      evidenceStatus: step.status
    },
    result: aggregateResponse(run).result,
    references: {
      status: { href: `/v1/caserun/runs/${run.runId}` },
      events: { href: `/v1/caserun/runs/${run.runId}/events`, count: run.cursor + 1 },
      aggregate: { href: `/v1/caserun/runs/${run.runId}/aggregate`, sha256: aggregateResponse(run).sha256 },
      manifest: step.status === "completed" ? { href: `/v1/caserun/runs/${run.runId}/manifest`, count: 2 } : null,
      replay: null,
      trace: { traceId: `trace-${run.runId}` },
      hwpod: { hwpodId: "native-hwpod", nodeId: "native-node" }
    },
    sourceAuthority: { mode: MODE, sequence: "request-driven" },
    error: null,
    statusUrl: `/v1/caserun/runs/${run.runId}`
  };
}

export function listNativeCases() {
  const cases = Object.entries(scenarios).map(([caseId, scenario]) => ({
    caseId,
    title: scenario.title,
    mode: MODE,
    available: true,
    hwpodSpec: "native-test",
    runtime: { sequence: scenario.steps.map((step) => step.status) }
  }));
  return { ok: true, mode: MODE, contractVersion: CONTRACT_VERSION, count: cases.length, cases, caseAuthority: { mode: MODE } };
}

export function startNativeRun(caseId: string) {
  const scenario = scenarios[caseId];
  if (!scenario) return null;
  runSequence += 1;
  const runId = `${caseId}-${runSequence}`;
  const run: NativeRunState = { runId, caseId, cursor: 0, steps: scenario.steps, createdAt: timestamp(30 + runSequence) };
  runs.set(runId, run);
  return runResponse(run);
}

export function listNativeRuns(query: { status?: string; limit?: number; cursor?: string } = {}) {
  const limit = query.limit ?? 30;
  const before = query.cursor ? decodeCursor(query.cursor) : null;
  const records = [...runs.values()]
    .map((run) => runResponse(run))
    .filter((run) => !query.status || run.status === query.status)
    .filter((run) => !before || run.createdAt < before.createdAt || (run.createdAt === before.createdAt && run.runId < before.runId))
    .sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.runId.localeCompare(left.runId));
  const page = records.slice(0, limit);
  const hasMore = records.length > limit;
  return {
    ok: true,
    mode: MODE,
    contractVersion: "hwlab-caserun-runs-v1",
    count: page.length,
    runs: page.map((run) => ({
      runId: run.runId,
      caseId: run.caseId,
      workflowId: run.workflowId,
      workflowRunId: run.workflowRunId,
      status: run.status,
      stage: run.stage,
      terminal: run.terminal,
      createdAt: run.createdAt,
      updatedAt: run.updatedAt,
      artifactCount: run.facts.artifactCount,
      evidenceStatus: run.facts.evidenceStatus,
      artifactManifestSha256: run.references.aggregate.sha256
    })),
    nextCursor: hasMore && page.length ? encodeCursor(page.at(-1)!) : null
  };
}

export function readNativeRun(runId: string, advance = true) {
  const run = runs.get(runId);
  if (!run) return null;
  if (advance && run.cursor < run.steps.length - 1) run.cursor += 1;
  return runResponse(run);
}

export function readNativeEvents(runId: string) {
  const run = runs.get(runId);
  if (!run) return null;
  const current = runResponse(run);
  const events = run.steps.slice(0, run.cursor + 1).map((step, index) => ({
    id: index + 1,
    at: timestamp(index),
    createdAt: timestamp(index),
    status: step.status,
    stage: step.stage,
    payload: { mode: MODE, terminal: step.terminal, blocker: step.blocker ?? null }
  }));
  return { ok: true, mode: MODE, contractVersion: CONTRACT_VERSION, runId, caseId: run.caseId, status: current.status, stage: current.stage, terminal: current.terminal, sourceAuthority: current.sourceAuthority, events };
}

export function readNativeAggregate(runId: string) {
  const run = runs.get(runId);
  return run ? aggregateResponse(run) : null;
}

export function resetNativeFixtures(): void {
  runSequence = 0;
  runs.clear();
  const steps = scenarios["native-completed"]!.steps;
  runs.set("cli-software-smoke-passed", {
    runId: "cli-software-smoke-passed",
    caseId: "software-smoke",
    cursor: steps.length - 1,
    steps,
    createdAt: timestamp(20)
  });
}

function encodeCursor(run: { createdAt: string; runId: string }): string {
  return Buffer.from(JSON.stringify({ createdAt: run.createdAt, runId: run.runId }), "utf8").toString("base64url");
}

function decodeCursor(cursor: string): { createdAt: string; runId: string } {
  const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
  return { createdAt: String(parsed.createdAt), runId: String(parsed.runId) };
}

resetNativeFixtures();
