import { env } from "@code/env/server";
import { Console, Effect, FiberSet, Result, Stream } from "effect";

import { makeAgentOsRuntime } from "./agent-os";
import { ConvexControlPlane, type DaemonCommand } from "./convex";

const errorMessage = (cause: unknown) =>
  cause instanceof Error ? cause.message : String(cause);

export const runDaemon = Effect.scoped(
  Effect.gen(function* () {
    const controlPlane = yield* ConvexControlPlane;
    const agentOs = yield* makeAgentOsRuntime;
    const sessionId = crypto.randomUUID();
    const hostname = yield* Effect.promise(() => import("node:os")).pipe(
      Effect.map((os) => os.hostname())
    );
    const fibers = yield* FiberSet.make<void>();

    yield* controlPlane.connect({
      sessionId,
      hostname,
      platform: process.platform,
      architecture: process.arch,
    });
    yield* Console.log(`daemon ${env.DAEMON_ID} connected as ${sessionId}`);

    yield* Effect.addFinalizer(() =>
      controlPlane
        .disconnect(sessionId)
        .pipe(
          Effect.catchCause((cause) =>
            Console.error("failed to mark daemon offline", cause)
          )
        )
    );

    yield* Stream.tick(env.DAEMON_HEARTBEAT_MS).pipe(
      Stream.runForEach(() => controlPlane.heartbeat(sessionId)),
      Effect.forkScoped
    );

    const processCommand = Effect.fn("Daemon.processCommand")(function* (
      candidate: DaemonCommand
    ) {
      const command = yield* controlPlane.claim(candidate._id, sessionId);
      if (!command) {
        return;
      }
      const started = yield* controlPlane.start(command._id, sessionId);
      if (!started) {
        return;
      }
      yield* controlPlane.recordEvent({
        commandId: command._id,
        kind: "command.started",
        data: { method: command.method, actorKey: command.actorKey },
      });
      const result = yield* agentOs.execute(command).pipe(Effect.result);
      if (Result.isSuccess(result)) {
        yield* controlPlane.succeed(command._id, sessionId, result.success);
        return;
      }
      yield* controlPlane.fail(
        command._id,
        sessionId,
        errorMessage(result.failure)
      );
    });

    yield* controlPlane.commands.pipe(
      Stream.runForEach((commands) =>
        Effect.forEach(
          commands,
          (command) => FiberSet.run(fibers, processCommand(command)),
          {
            discard: true,
          }
        )
      )
    );
  })
);
