import * as NodeFSP from "node:fs/promises";
import * as NodeHttp from "node:http";
import * as NodePath from "node:path";
import { execFileSync } from "node:child_process";
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
import * as NodeServices from "@effect/platform-node/NodeServices";
import * as NetService from "@t3tools/shared/Net";
import * as Effect from "effect/Effect";
import * as Cause from "effect/Cause";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as References from "effect/References";
const home = process.argv[2]!;
const runPromise = (effect) => Effect.runPromise(effect.pipe(Effect.provide(NodeServices.layer)));
const ROOT = "ses_scenario_root";
const CHILD = "ses_scenario_child";
const MODEL = { providerID: "openai", modelID: "gpt-5.4-mini" };

function fakeOpenCode() {
  const stamp = Date.now();
  const streams = new Set<NodeHttp.ServerResponse>();
  let sequence = 0;
  const emit = (type: string, properties: unknown) => {
    const frame = `data: ${JSON.stringify({ id: `evt_${++sequence}`, type, properties })}\n\n`;
    for (const stream of streams) stream.write(frame);
  };
  const session = (id: string) => ({
    id,
    slug: id,
    version: "1.19.5",
    projectID: "global",
    directory: "/tmp",
    title: id,
    time: { created: stamp, updated: stamp },
    ...(id === CHILD ? { parentID: ROOT } : {}),
  });
  const user = (sessionID: string, id: string) =>
    emit("message.updated", {
      sessionID,
      info: { id, sessionID, role: "user", time: { created: stamp }, agent: "build", model: MODEL },
    });
  const answer = (sessionID: string, id: string, parentID: string, text: string) => {
    const info = {
      id,
      parentID,
      sessionID,
      role: "assistant",
      mode: "build",
      agent: "build",
      path: { cwd: "/tmp", root: "/" },
      cost: 0,
      tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
      ...MODEL,
    };
    emit("message.updated", { sessionID, info: { ...info, time: { created: stamp } } });
    emit("message.part.updated", {
      sessionID,
      part: { id: `prt_${id}`, messageID: id, sessionID, type: "text", text },
      time: stamp + 1,
    });
    emit("message.updated", {
      sessionID,
      info: { ...info, time: { created: stamp, completed: stamp + 2 }, finish: "stop" },
    });
  };
  const status = (sessionID: string, type: "busy" | "idle") => {
    emit("session.status", { sessionID, status: { type } });
    if (type === "idle") emit("session.idle", { sessionID });
  };
  const task = (state: "running" | "completed") =>
    emit("message.part.updated", {
      sessionID: ROOT,
      part: {
        id: "prt_task",
        messageID: "msg_tool_step",
        sessionID: ROOT,
        type: "tool",
        tool: "task",
        callID: "call_task",
        state: {
          status: state,
          title: "Read math.py",
          input: {
            description: "Read math.py",
            prompt: "Read math.py and report its add function.",
            subagent_type: "general",
            background: true,
          },
          metadata: { sessionId: CHILD, background: true, jobId: CHILD },
          time: { start: stamp, ...(state === "completed" ? { end: stamp + 1 } : {}) },
          ...(state === "completed" ? { output: "Background task started" } : {}),
        },
      },
      time: stamp,
    });

  const runPrompt = (messageID: string) => {
    user(ROOT, messageID);
    status(ROOT, "busy");
    emit("session.created", { sessionID: CHILD, info: session(CHILD) });
    task("running");
    user(CHILD, "msg_child_prompt");
    task("completed");
    emit("session.status", { sessionID: CHILD, status: { type: "busy" } });
    answer(CHILD, "msg_child_answer", "msg_child_prompt", "math.py defines add(a, b), returning a + b.");
    status(CHILD, "idle");
    answer(ROOT, "msg_root_answer", messageID, "PARENT_OK");
    status(ROOT, "idle");
  };
  const server = NodeHttp.createServer(async (request, response) => {
    const path = new URL(request.url ?? "/", "http://opencode").pathname;
    let body = "";
    for await (const chunk of request) body += chunk;
    const json = (value: unknown) => {
      response.writeHead(200, { "content-type": "application/json" });
      response.end(JSON.stringify(value));
    };
    if (path === "/event") {
      response.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache" });
      response.write(`data: ${JSON.stringify({ type: "server.connected", properties: {} })}\n\n`);
      streams.add(response);
      request.on("close", () => streams.delete(response));
      return;
    }
    if (path === "/api/info") {
      response.writeHead(200, { "content-type": "text/html" });
      return response.end("<!doctype html>");
    }
    if (path === "/global/health") return json({ healthy: true, version: "1.19.5" });
    if (path === "/provider") {
      const model = { id: MODEL.modelID, name: "GPT-5.4 mini", release_date: "2026-01-01" };
      return json({
        all: [{ id: MODEL.providerID, name: "OpenAI", models: { [model.id]: model } }],
        default: { [MODEL.providerID]: model.id },
        connected: [MODEL.providerID],
      });
    }
    if (path === "/agent") return json([{ name: "build", mode: "primary" }]);
    if (path === "/command" || path === "/skill") return json([]);
    if (path === "/session/status") return json({});
    if (path === "/session" && request.method === "POST") return json(session(ROOT));
    const match = /^\/session\/([^/]+)(?:\/([a-z_]+))?$/.exec(path);
    if (match !== null) {
      const [, id = ROOT, action] = match;
      if (action === undefined) return json(session(id));
      if (action === "children") return json([]);
      if (action === "abort") return json(true);
      if (action === "prompt_async") {
        response.writeHead(204).end();
        return runPrompt(JSON.parse(body).messageID);
      }
    }
    response.writeHead(404, { "content-type": "application/json" });
    response.end("{}");
  });
  return { server };
}

async function remoteLaunch(project: string) {
  const source = (path: string) => import(`./src/${path}`);
  const [Auth, Config, CliConfig, Bridge, Runtime] = await Promise.all([
    source("auth/EnvironmentAuth.ts"), source("config.ts"), source("cli/config.ts"),
    source("mcp/AcpMcpStdioBridge.ts"), source("serverRuntimeState.ts"),
  ]);
  return runPromise(Effect.gen(function* () {
    
    const config = yield* CliConfig.resolveCliAuthConfig({ baseDir: Option.some(home) }, Option.none());
    
    return yield* Effect.gen(function* () {
      const auth = yield* Auth.EnvironmentAuth;
      const issued = yield* auth.issueMcpClientSession({ label: "T3 Code · A", access: "full-access", peerLink: true, client: { deviceType: "bot" } });
      
      const state = yield* Runtime.readLiveServerRuntimeState(config.serverRuntimeStatePath);
      const session = yield* Bridge.mcpToolSession({ endpoint: new URL("/mcp", state.value.origin).href, authorization: `Bearer ${issued.token}`, clientName: "scenario-remote-delegation" });
      
      const projects = yield* session.call("t3_project_list", {});
      const projectId = projects.structuredContent.projects.find((p: { workspaceRoot: string }) => p.workspaceRoot === project).id;
      const result = yield* session.call("t3_thread_launch", {
        projectId, title: "Parent on A", modelSelection: { instanceId: "opencode", model: "openai/gpt-5.4-mini" },
        message: "Use one general subagent to read math.py.", runtimeMode: "full-access",
        delegatedFrom: { environmentId: "scenario-environment-a", threadId: "scenario-parent-a", title: "Parent on A" },
      });
      if (result.isError) throw new Error(JSON.stringify(result));
      const parent = result.structuredContent as { threadId: string };
      yield* session.call("t3_thread_wait", { threadId: parent.threadId, timeoutMs: 45000 });
      const listed = yield* session.call("t3_thread_list", { projectId, includeSubagents: true, limit: 100 });
      const child = listed.structuredContent.threads.find((t: { title: string }) => t.title === "Read math.py");
      if (!child) throw new Error(`Native child missing: ${JSON.stringify(listed)}`);
      return child as { threadId: string };
    }).pipe(Effect.scoped, Effect.provide(Auth.layerRuntime.pipe(Layer.provide(Config.layer(config)), Layer.provide(Layer.succeed(References.MinimumLogLevel, config.logLevel)))));
  }).pipe(Effect.provide([NodeCrypto.layer, NetService.layer]), Effect.tapCause((cause) => Effect.sync(() => console.error(Cause.pretty(cause))))));
}

const opencode = fakeOpenCode();
await new Promise<void>((resolve) => opencode.server.listen(0, "127.0.0.1", resolve));
const port = opencode.server.address().port;
const settingsPath = NodePath.join(home, "userdata/settings.json");
const settings = JSON.parse(await NodeFSP.readFile(settingsPath, "utf8"));
settings.linkedEnvironmentsEnabled = true;
settings.providerInstances.opencode = { driver: "opencode", enabled: true, config: { serverUrl: `http://127.0.0.1:${port}` } };
await NodeFSP.writeFile(settingsPath, JSON.stringify(settings));
const project = NodePath.join(home, "workspaces/repoB");
await NodeFSP.mkdir(project, { recursive: true });
await NodeFSP.writeFile(NodePath.join(project, "math.py"), "def add(a, b):\n    return a + b\n");
const git = (args) => execFileSync("git", args, { cwd: project });
git(["init", "--quiet", "--initial-branch=main"]); git(["add", "."]);
git(["-c", "user.name=Scenario", "-c", "user.email=scenario@example.invalid", "commit", "--quiet", "--allow-empty", "-m", "Initial commit"]);
try { execFileSync(process.execPath, ["apps/server/src/bin.ts", "project", "add", project, "--title", "repoB", "--base-dir", home], { cwd: NodePath.resolve(import.meta.dirname, "../..") }); } catch (error) { if (!String(error.stdout).includes("ProjectAlreadyExistsError")) throw error; }
const child = await remoteLaunch(project);
await NodeFSP.writeFile(NodePath.join(home, "child.json"), JSON.stringify(child));
console.log("SEEDED", child.threadId);
