import * as NodeCrypto from "node:crypto";
import * as NodeFS from "node:fs";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Stream from "effect/Stream";
import { Socket } from "effect/socket";
import { RpcClient, RpcSerialization } from "effect/rpc";
import { ORCHESTRATION_PROTOCOL_VERSION, ORCHESTRATION_PROTOCOL_HEADER, CommandId, ProjectId, ProviderInstanceId, ThreadId, WsRpcGroup } from "@t3tools/contracts";

const S = process.env.EVIDENCE_SCRATCH!;
const model = process.argv[2] ?? "gpt-5.6-sol";
const port = Number(process.env.PORT); const origin = `http://127.0.0.1:${port}`;
const ready = Promise.resolve(); const stop = () => {};
try {
  await ready;
  const tok = { access_token: process.env.TOK! };
  const headers = { Authorization: `Bearer ${tok.access_token}`, "Content-Type": "application/json", [ORCHESTRATION_PROTOCOL_HEADER]: String(ORCHESTRATION_PROTOCOL_VERSION) };
  const projectId = ProjectId.make(NodeCrypto.randomUUID());
  const c = await fetch(`${origin}/api/projects/mutate`, { method: "POST", headers, body: JSON.stringify({ type: "project.create", commandId: NodeCrypto.randomUUID(), projectId, title: "repro 24790", workspaceRoot: process.env.PROJ ?? `${S}/project` }) });
  if (!c.ok) throw new Error(`project ${c.status} ${await c.text()}`);
  const ticket = await (await fetch(`${origin}/api/auth/websocket-ticket`, { method: "POST", headers })).json() as any;
  const layer = RpcClient.layerProtocolSocket().pipe(Layer.provide(Socket.layerWebSocket(`ws://127.0.0.1:${port}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket.ticket)}`).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal))), Layer.provide(RpcSerialization.layerJson));
  const threadId = ThreadId.make(NodeCrypto.randomUUID());
  NodeFS.writeFileSync(`${S}/evidence/after-thread-id`, threadId);
  await Effect.runPromise(Effect.gen(function* () {
    const client = yield* RpcClient.make(WsRpcGroup);
    const subscribed = new Set<string>();
    const frames: Record<string, unknown[]> = {};
    const fetched: unknown[] = [];
    const save = () => NodeFS.writeFileSync(`${S}/evidence/after-wire.json`, JSON.stringify({frames, fetched}, null, 2));
    yield* client["orchestration.launchThread"]({ commandId: CommandId.make(NodeCrypto.randomUUID()), threadId, projectId, title: "repro", modelSelection: { instanceId: ProviderInstanceId.make("codex"), model }, runtimeMode: "full-access", interactionMode: "default", workspaceStrategy: { type: "root" }, initialMessage: { text: "Run exactly this shell command once and nothing else: python3 fixture.py . It prints only two named synthetic fixture variables and ordinary text. Do not inspect files or run env, set, export, or printenv without arguments. Then reply DONE.", attachments: [] } });
    yield* client["orchestration.subscribeThread"]({ threadId }).pipe(Stream.takeUntilEffect(() => Effect.gen(function* () {
      const p = yield* client["orchestration.getThreadProjection"]({ threadId });
      NodeFS.writeFileSync(`${S}/evidence/after-projection.json`, JSON.stringify(p, null, 2));
      for (const item of p.turnItems) {
        if (item.type !== "command_execution" || subscribed.has(item.id)) continue;
        subscribed.add(item.id);
        frames[item.id] = [];
        yield* client["orchestration.subscribeCommandOutput"]({threadId, itemId: item.id}).pipe(
          Stream.runForEach(frame => Effect.sync(() => { frames[item.id].push(frame); save(); })),
          Effect.catch(error => Effect.sync(() => { frames[item.id].push({error: String(error)}); save(); })),
          Effect.forkScoped);
      }
      if (p.runs.length > 0 && p.runs.every((x: any) => ["completed", "failed", "cancelled", "interrupted"].includes(x.status))) {
        for (const item of p.turnItems) if (item.type === "command_execution") {
          fetched.push(yield* client["orchestration.getTurnItem"]({threadId, itemId: item.id}));
        }
        save();
      }
      return p.runs.length > 0 && p.runs.every((x: any) => ["completed", "failed", "cancelled", "interrupted"].includes(x.status));
    })), Stream.runDrain, Effect.timeout(240_000));
  }).pipe(Effect.scoped, Effect.provide(layer)));
  console.log("turn finished");
} finally { stop();   }
