// Standalone evidence harness; synthetic ACP subprocess, real registry adapter/runtime.
// Copy beside AcpAdapterV2.testkit.ts in an isolated checkout; never commit this file.
import * as NodeFS from "node:fs";
import * as NodePath from "node:path";
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, it } from "@effect/vitest";
import { ProviderInstanceId, ProviderSessionId, ThreadId, type ModelSelection } from "@t3tools/contracts";
import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import * as DateTime from "effect/DateTime";
import * as FileSystem from "effect/FileSystem";
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
import * as Stream from "effect/Stream";
import * as Logger from "effect/Logger";
import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions";
import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost";
import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";
import * as AcpRegistryRuntimeCoordinator from "@t3tools/provider-acp-registry/server/AcpRegistryRuntimeCoordinator";
import { AcpRegistryAdapterV2Driver } from "@t3tools/provider-acp-registry/server";
import { ACP_REGISTRY_PROVIDER, makeAcpRegistryAdapterV2 } from "@t3tools/provider-acp-registry/testing";
import * as ServerSettings from "../../serverSettings.ts";
import { decodeAcpReplayTranscript, makeAcpReplayRuntime, makeAcpReplayCompletenessAssertion, makeTurnInput } from "./AcpAdapterV2.testkit.ts";

type Frame = Record<string, unknown>;
const outbound = (method: string, params: unknown = "<any>"): Frame => ({ type: "expect_outbound", frame: { kind: "request", method, params } });
const answer = (method: string, result: unknown): Frame => ({ type: "emit_inbound", frame: { kind: "response", method, result } });
const errorAnswer = (code: number): Frame => ({ type: "emit_inbound", frame: { kind: "response", method: "session/set_config_option", error: { code, message: code === -32602 ? "fixture value refused" : "fixture temporary failure" } } });
const option = (id: string, currentValue: string, values: string[], category?: string) => ({ id, name: id, ...(category ? { category } : {}), type: "select", currentValue, options: values.map(value => ({ value, name: value })) });
const model = (value: string) => option("model", value, ["default", "named"], "model");
const options = (m: string, t = "low", later = "off") => [model(m), option("thinking", t, ["low", "high"]), option("later", later, ["off", "on"])];
const set = (sessionId: string, configId: string, value: string) => outbound("session/set_config_option", { sessionId, configId, value });
const setOk = (sessionId: string, id: string, value: string, configOptions: unknown[]) => [set(sessionId, id, value), answer("session/set_config_option", { configOptions })];
const prompt = () => [outbound("session/prompt"), answer("session/prompt", { stopReason: "end_turn" })];
const optionalPair = (frames: Frame[]) => [{ ...frames[0], label: "optional-pair" }, frames[1]];
const root = "/tmp/lead-33118-r1/evidence-capture";
const runDir = process.env.EVIDENCE_RUN_DIR!;
const sha = process.env.EVIDENCE_SHA!;
const side = process.env.EVIDENCE_SIDE!;
if (!runDir || !sha || !side) throw new Error("Set EVIDENCE_RUN_DIR, EVIDENCE_SHA, EVIDENCE_SIDE");
NodeFS.mkdirSync(runDir, { recursive: true });
const layerHost = TestProviderHost.layer().pipe(Layer.provide(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer)));
const layerTest = Layer.mergeAll(NodeServices.layer, NodeCrypto.layer, IdAllocator.layer, McpProviderSessions.layer, layerHost, ServerSettings.layerTest());
const fixtureNames = ["A-R1-R2-R4-R10", "B-R3", "C-R6", "D-R7-load", "E-R8", "F-R9", "G-R7-resume"];

function failureDetails(error: unknown): unknown {
  if (error === null || typeof error !== "object") return error;
  const e = error as Record<string, unknown>;
  return { tag: e._tag, message: e.message, code: e.code, errorMessage: e.errorMessage,
    ...(e.cause === undefined ? {} : { cause: failureDetails(e.cause) }) };
}

for (const fixture of fixtureNames) {
  it.live(`harness transcript ${fixture}`, () => {
    const file = NodePath.join(runDir, `${fixture}.ndjson`);
    const records: Record<string, unknown>[] = [];
    let phase = "setup";
    let callbacks = 0;
    let seq = 0;
    const log = (kind: string, data: Record<string, unknown> = {}) => {
      const record = { seq: ++seq, fixture, phase, kind, ...data };
      records.push(record);
      NodeFS.appendFileSync(file, `${JSON.stringify(record)}\n`);
      console.log(JSON.stringify(record));
    };
    log("harness", { side, sha, label: "synthetic ACP process; real ACP registry adapter and runtime; no T3 server, database, UI, or upstream provider", node: process.version });
    const logger = Logger.make(({ message, logLevel }) => {
      if (logLevel === "Warn" || logLevel === "Error" || logLevel === "Fatal") log("warning", { level: logLevel, message });
    });
    const program = Effect.gen(function* () {
      const fs = yield* FileSystem.FileSystem;
      const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
      const exits: Effect.Effect<void>[] = [];
      const childProcessSpawner = ChildProcessSpawner.make(command => spawner.spawn(command).pipe(Effect.tap(handle => Effect.sync(() => { exits.push(Effect.ignore(handle.exitCode)); }))));
      const isA = fixture.startsWith("A-");
      const isB = fixture.startsWith("B-");
      const isC = fixture.startsWith("C-");
      const isCold = fixture.startsWith("D-") || fixture.startsWith("G-");
      const isResumeMethod = fixture.startsWith("G-");
      const isE = fixture.startsWith("E-");
      const isF = fixture.startsWith("F-");
      const sid = "fixture-A";
      const targetSid = isE || isF ? "fixture-B" : sid;
      const initialOptions = isA ? [model("default")] : options("default");
      const startup = isCold ? (isResumeMethod ? "session/resume" : "session/load") : "session/new";
      const entries: Frame[] = [
        outbound("initialize"),
        answer("initialize", { protocolVersion: 1, agentCapabilities: { loadSession: !isResumeMethod, ...(isResumeMethod ? { sessionCapabilities: { resume: {} } } : {}) }, authMethods: [{ id: "test", name: "Test" }] }),
        outbound(startup), answer(startup, { ...(isCold ? {} : { sessionId: sid }), configOptions: initialOptions }),
      ];
      if (isA) entries.push(...setOk(sid, "model", "named", options("named")));
      if (isE || isF) entries.push(outbound("session/load"), answer("session/load", { configOptions: options("default") }));
      entries.push(set(targetSid, "thinking", "high"), errorAnswer(isB ? -32602 : -32603));
      if (isA || isF) {
        const m = isA ? "named" : "default";
        // Before continues and prompts; after fails and retries. Same input admits either branch.
        entries.push(...optionalPair(setOk(targetSid, "later", "on", options(m, "low", "on"))));
        entries.push(...optionalPair(prompt()));
        entries.push(...optionalPair(setOk(targetSid, "thinking", "high", options(m, "high"))));
        entries.push(...optionalPair(setOk(targetSid, "later", "on", options(m, "high", "on"))));
        entries.push(...prompt());
      } else if (isB) {
        entries.push(...setOk(sid, "later", "on", options("default", "low", "on")), ...prompt());
      }
      const transcript = yield* decodeAcpReplayTranscript({ provider: ACP_REGISTRY_PROVIDER, protocol: "acp.ndjson-jsonrpc", version: "1", scenario: fixture, entries: entries as never }, ACP_REGISTRY_PROVIDER);
      const statusPath = NodePath.join(runDir, `${fixture}.status.json`);
      const instanceId = ProviderInstanceId.make("fixture-acp-registry");
      const threadId = ThreadId.make(`fixture-thread-${fixture}`);
      const picked = (modelId: string, withOptions: boolean): ModelSelection => ({ instanceId, model: modelId, ...(withOptions ? { options: [{ id: "thinking", value: "high" }, { id: "unavailable", value: "x" }, ...((isA || isB || isF) ? [{ id: "later", value: "on" }] : [])] } : {}) });
      const stored = picked("default", isC || isCold);
      const target = picked(isA ? "named" : "default", true);
      const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ runtimeMode: "approval-required", interactionMode: "default", cwd: runDir });
      const replayRuntime = makeAcpReplayRuntime({ transcript, statusPath, scriptPath: NodePath.join(root, "branching-acp-replay-agent.ts"), childProcessSpawner, fileSystem: fs });
      const adapter = yield* makeAcpRegistryAdapterV2({
        selfInvocation: yield* resolveSelfInvocation(), instanceId,
        settings: yield* Schema.decodeUnknownEffect(AcpRegistryAdapterV2Driver.configSchema)({ agentId: "fixture-agent", authMethodId: "test" }), environment: {},
        makeRuntime: runtimeInput => replayRuntime({ ...runtimeInput,
          protocolLogging: { logIncoming: true, logOutgoing: true, logger: event => Effect.sync(() => {
            if (event.stage === "raw") log("protocol", { direction: event.direction, payload: event.payload });
          }) },
          requestLogger: event => Effect.sync(() => { log("request", { method: event.method, status: event.status, ...(event.status === "failed" ? { cause: event.cause } : {}) }); }),
        }),
      }).pipe(Effect.provide(Layer.mergeAll(
        Layer.mock(AcpRegistrySupport.AcpRegistryCatalog)({ resolve: () => Effect.die("runtime injected") }),
        Layer.mock(AcpRegistryRuntimeCoordinator.AcpRegistryRuntimeCoordinator)({
          withForegroundStartup: (_key, effect) => effect,
          publishLiveConfiguration: (_id, configuration) => Effect.sync(() => { log("configuration_callback", { count: ++callbacks, configuration }); }),
        }),
      )));
      yield* Effect.gen(function* () {
        phase = "openSession";
        log("call", { method: phase, nativeSessionId: isCold ? sid : "new", modelSelection: stored });
        const opened = yield* adapter.openSession({ threadId, providerSessionId: ProviderSessionId.make(`fixture-provider-${fixture}`), modelSelection: stored, runtimePolicy, ...(isCold ? { initialNativeThreadId: sid } : {}) }).pipe(Effect.result);
        log("result", { method: phase, result: opened._tag, ...(opened._tag === "Failure" ? { error: failureDetails(opened.failure) } : { providerSessionStatus: opened.success.providerSession.status }), callbacks });
        if (opened._tag === "Failure") return;
        const runtime = opened.success;
        if (isC || isCold) return;
        const providerThread = yield* runtime.ensureThread({ threadId, modelSelection: stored, runtimePolicy });
        log("ensureThread", { nativeThreadRef: providerThread.nativeThreadRef });
        const targetThread = isE || isF ? { ...providerThread, nativeThreadRef: { driver: ACP_REGISTRY_PROVIDER, nativeId: targetSid, strength: "strong" as const } } : providerThread;
        if (isE) {
          phase = "resumeThread";
          log("call", { method: phase, nativeSessionId: targetSid, modelSelection: target });
          const resumed = yield* runtime.resumeThread({ providerThread: targetThread, threadId, modelSelection: target, runtimePolicy }).pipe(Effect.result);
          log("result", { method: phase, result: resumed._tag, ...(resumed._tag === "Failure" ? { error: failureDetails(resumed.failure) } : { nativeThreadRef: resumed.success.nativeThreadRef }), callbacks });
          return;
        }
        const now = yield* DateTime.now;
        for (let ordinal = 1; ordinal <= (isB ? 1 : 2); ordinal++) {
          phase = `startTurn-${ordinal}`;
          log("call", { method: "startTurn", ordinal, nativeSessionId: targetSid, modelSelection: target, callbacks });
          const started = yield* runtime.startTurn(makeTurnInput({ threadId, providerThread: targetThread, instanceId, runtimePolicy, now, ordinal, modelSelection: target, messageText: "Synthetic evidence prompt" })).pipe(Effect.result);
          log("result", { method: "startTurn", ordinal, result: started._tag, ...(started._tag === "Failure" ? { error: failureDetails(started.failure) } : {}), callbacks });
          if (started._tag === "Failure") continue;
          yield* runtime.events.pipe(Stream.takeUntil(event => event.type === "turn.terminal"), Stream.runForEach(event => Effect.sync(() => {
            if (event.type === "turn.terminal") { log("terminal", { status: event.status, failure: event.failure }); assert.equal(event.status, "completed"); }
          })));
        }
      }).pipe(Effect.scoped);
      phase = "cleanup";
      yield* Effect.all(exits, { discard: true });
      yield* makeAcpReplayCompletenessAssertion(fs, statusPath, transcript);
      log("replay_complete", { status: JSON.parse(NodeFS.readFileSync(statusPath, "utf8")), callbacks });
    }).pipe(Effect.provide(layerTest), Effect.provide(Logger.layer([logger], { mergeWithExisting: false })), Effect.scoped);
    return program;
  });
}
