import assert from "node:assert/strict";
import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "bun:test";
import { readFileSync } from "node:fs";

import { hwlabRuntimeLaneSpecForNode } from "../hwlab-node-lanes";
import { rootPath } from "../config";
import { buildWebObserveCommandVisibility, nodeWebProbeRealtimeFanoutProfiles, nodeWebProbeWorkbenchKafkaDebugReplay, nodeWebProbeWorkbenchTraceReadability, resolveWebObserveActionJson, webObserveStopFallbackReason } from "./web-probe-observe-actions";

test("realtime authority runner contract derives topics, groups, and transactional capabilities from owning YAML", () => {
  const profiles = nodeWebProbeRealtimeFanoutProfiles(hwlabRuntimeLaneSpecForNode("v03", "NC01"));
  const expectedKafka = profiles["pure-kafka-live"]?.expectedKafka as Record<string, any>;
  assert.deepEqual(expectedKafka.topics, {
    stdio: "codex-stdio.raw.v1",
    agentrun: "agentrun.event.v1",
    hwlab: "hwlab.event.v1",
  });
  assert.deepEqual(expectedKafka.groups, {
    transactionalProjector: "hwlab-v03-agentrun-event-projector",
    liveSse: "hwlab-v03-workbench-live-sse",
  });
  assert.deepEqual(expectedKafka.capabilities, {
    transactionalProjector: false,
    projectionOutboxRelay: false,
    projectionRealtime: false,
  });
  const kafka = hwlabRuntimeLaneSpecForNode("v03", "NC01").codeAgentRuntime?.kafkaEventBridge;
  assert.deepEqual(kafka?.features, {
    directPublish: true,
    liveKafkaSse: true,
    kafkaRefreshReplay: true,
    transactionalProjector: false,
    projectionOutboxRelay: false,
    projectionRealtime: false,
  });
  assert.deepEqual(profiles["pure-kafka-live"]?.requiredEventTypes, {
    agentrun: ["user_message", "assistant_message", "terminal_status"],
    hwlab: ["user", "assistant", "terminal"],
  });
  assert.deepEqual(profiles["pure-kafka-live"]?.conditionalEventTypePairs, {
    "tool-call": { agentrun: "tool_call", hwlab: "tool" },
    "command-output": { agentrun: "command_output", hwlab: "status" },
  });
});

test("realtime authority parser and inline render expose no direct authority or duplicate native postprocess execution", () => {
  const laneSource = readFileSync(rootPath("scripts", "src", "hwlab-node-lanes.ts"), "utf8");
  const renderSource = readFileSync(rootPath("scripts", "src", "hwlab-node", "render.ts"), "utf8");
  assert.doesNotMatch(laneSource, /direct-live/u);
  assert.equal(renderSource.includes("UNIDESK_RUNTIME_GITOPS_RUNTIME_PATH"), false);
  assert.equal(renderSource.includes('UNIDESK_RUNTIME_GITOPS_OVERLAY_B64="$overlay_b64" UNIDESK_RUNTIME_GITOPS_RUNTIME_PATH'), false);
  assert.match(renderSource, /runtimeGitopsPostprocessScript\(\)/u);
  assert.match(renderSource, /writeRuntimeGitopsNativeScript\("runtime-gitops-postprocess\.mjs"/u);
});

test("Workbench Kafka debug replay runner contract is derived from owning YAML", () => {
  assert.deepEqual(nodeWebProbeWorkbenchKafkaDebugReplay(hwlabRuntimeLaneSpecForNode("v03", "NC01")), {
    enabled: true,
    topic: "hwlab.event.debug.v1",
    groupPrefix: "hwlab-v03-workbench-isolated-debug",
    completionTimeoutMs: 30_000,
    productEventsPath: "/v1/workbench/events",
    debugEventsPath: "/v1/workbench/debug/kafka-sse/events",
    requestObservationQuietMs: 3_000,
    valuesRedacted: true,
  });
});

test("Workbench 产品 Trace 可读性 runner 合同完全来自独立 owning YAML", () => {
  assert.deepEqual(nodeWebProbeWorkbenchTraceReadability(hwlabRuntimeLaneSpecForNode("v03", "NC01")), {
    enabled: true,
    runningCardTimeoutMs: 15_000,
    disclosureActionTimeoutMs: 15_000,
    disclosureOpenTimeoutMs: 10_000,
    runningReadableTimeoutMs: 480_000,
    terminalTimeoutMs: 480_000,
    domStableQuietMs: 2_000,
    domStableTimeoutMs: 6_000,
    pollIntervalMs: 100,
    valuesRedacted: true,
  });
});

test("web observe command visibility does not equate control completion with async turn completion", () => {
  const visibility = buildWebObserveCommandVisibility({
    commandType: "sendPrompt",
    exitCode: 0,
    commandResult: {
      ok: true,
      completedAt: "2026-07-10T01:00:01.000Z",
      result: {
        chatSubmit: {
          status: 202,
          traceId: "trc_fixture",
          otelTraceId: "otel_fixture",
        },
      },
    },
  });
  assert.equal(visibility.status, "control-completed");
  assert.equal(visibility.control.executionStatus, "completed");
  assert.equal(visibility.control.businessTurnTerminalImplied, false);
  assert.equal(visibility.asyncTurn.submissionStatus, "accepted");
  assert.equal(visibility.asyncTurn.terminalStatus, null);
  assert.equal(visibility.asyncTurn.terminalObserved, false);
  assert.equal(visibility.asyncTurn.terminalStatusSource, "not-evaluated-by-control-command");
});

test("observe stop automatically falls back when graceful cleanup is not proven", () => {
  assert.equal(webObserveStopFallbackReason({
    stopCommand: true,
    exitCode: 255,
    commandResult: null,
  }), "graceful-stop-failed");
  assert.equal(webObserveStopFallbackReason({
    stopCommand: true,
    exitCode: 0,
    commandResult: null,
  }), "graceful-stop-contract-missing");
  assert.equal(webObserveStopFallbackReason({
    stopCommand: true,
    exitCode: 0,
    commandResult: { ok: true, queued: true, waitTimedOut: true },
  }), "graceful-stop-not-consumed");
  assert.equal(webObserveStopFallbackReason({
    stopCommand: true,
    exitCode: 0,
    commandResult: { ok: true, queued: false, status: "done" },
  }), null);
  assert.equal(webObserveStopFallbackReason({
    stopCommand: false,
    exitCode: 255,
    commandResult: null,
  }), null);
});

test("web observe action JSON recovery accepts concrete start contract", () => {
  const resolved = resolveWebObserveActionJson({
    stdout: JSON.stringify({
      ok: true,
      command: "web-probe-observe start",
      jobId: "webobs-fixture",
      stateDir: ".state/web-observe/JD01/v03/fixture",
      valuesRedacted: true,
    }),
    stderr: "",
    exitCode: 0,
    timedOut: false,
  }, "start");

  assert.equal(resolved.source, "stdout");
  assert.equal(resolved.parsed?.jobId, "webobs-fixture");
  assert.equal(resolved.diagnostics.stdoutContractAccepted, true);
});

test("web observe action JSON recovery accepts bounded performance capture command summary", () => {
  const resolved = resolveWebObserveActionJson({
    stdout: JSON.stringify({
      ok: true,
      queued: false,
      commandId: "cmd-performance",
      type: "performanceCapture",
      status: "done",
      result: {
        captureId: "cpu-fixture",
        profileSha256: "sha256:profile",
        reportSha256: "sha256:report",
        valuesRedacted: true,
      },
      valuesRedacted: true,
    }),
    stderr: "",
    exitCode: 0,
    timedOut: false,
  }, "command");

  assert.equal(resolved.source, "stdout");
  assert.equal(resolved.parsed?.commandId, "cmd-performance");
  assert.equal(resolved.diagnostics.stdoutContractAccepted, true);
});

test("web observe action JSON recovery reads dump wrapper for status contract", async () => {
  const dir = await mkdtemp(join(tmpdir(), "unidesk-web-observe-status-dump-"));
  const dumpPath = join(dir, "status.json");
  await writeFile(dumpPath, JSON.stringify({
    ok: true,
    status: "running",
    jobId: "webobs-fixture",
    heartbeat: { status: "running" },
    diagnostics: { heartbeatStale: false },
  }) + "\n");

  const resolved = resolveWebObserveActionJson({
    stdout: JSON.stringify({
      ok: true,
      data: {
        outputTruncated: true,
        reason: "stdout-json-bytes-exceeded-threshold",
        dump: { path: dumpPath },
      },
    }),
    stderr: "",
    exitCode: 0,
    timedOut: false,
  }, "status");

  assert.equal(resolved.source, "dump");
  assert.equal(resolved.parsed?.jobId, "webobs-fixture");
  assert.equal(resolved.diagnostics.stdoutKind, "dump-wrapper");
  assert.equal(resolved.diagnostics.dumpPath, dumpPath);
});

test("web observe action JSON recovery reads SSH truncation summary dump for command contract", async () => {
  const dir = await mkdtemp(join(tmpdir(), "unidesk-web-observe-command-dump-"));
  const dumpPath = join(dir, "command.json");
  await writeFile(dumpPath, JSON.stringify({
    ok: true,
    queued: false,
    waitTimedOut: false,
    commandId: "cmd-fixture",
  }) + "\n");
  const summary = {
    code: "ssh-truncation-summary",
    stdout: {
      stream: "stdout",
      dumpPath,
      forwardedBytes: 10240,
      thresholdBytes: 10240,
    },
  };

  const resolved = resolveWebObserveActionJson({
    stdout: `UNIDESK_SSH_TRUNCATION_SUMMARY ${JSON.stringify(summary)}\n`,
    stderr: "",
    exitCode: 0,
    timedOut: false,
  }, "command");

  assert.equal(resolved.source, "dump");
  assert.equal(resolved.parsed?.commandId, "cmd-fixture");
  assert.equal(resolved.diagnostics.stdoutKind, "ssh-truncation-summary");
  assert.equal(resolved.diagnostics.dumpPath, dumpPath);
});

test("web observe action JSON recovery rejects ok-only action contract", () => {
  const resolved = resolveWebObserveActionJson({
    stdout: JSON.stringify({ ok: true }),
    stderr: "",
    exitCode: 0,
    timedOut: false,
  }, "start");

  assert.equal(resolved.parsed, null);
  assert.equal(resolved.source, null);
  assert.equal(resolved.diagnostics.stdoutKind, "json");
  assert.equal(resolved.diagnostics.fallbackReason, "stdout-json-contract-invalid");
  assert.equal(resolved.diagnostics.stdoutContractAccepted, false);
});
