import codex from "@agentos-software/codex-cli";
import git from "@agentos-software/git";
import opencode from "@agentos-software/opencode";
import pi from "@agentos-software/pi";
import { createSandboxSessionEnv } from "@flue/runtime";
import type { FileStat, SandboxApi, SandboxFactory } from "@flue/runtime";
import { AgentOs, createHostDirBackend } from "@rivet-dev/agentos-core";
import type { AgentOsOptions, SoftwareInput } from "@rivet-dev/agentos-core";

const WORK_SOFTWARE: SoftwareInput[] = [git, opencode, codex, pi];

export interface WorkVmOptions {
  readonly extraSoftware?: SoftwareInput[];
  readonly worktreePath: string;
}

const statResult = (s: {
  isDirectory: boolean;
  isSymbolicLink: boolean;
  mtimeMs: number;
  size: number;
}): FileStat => ({
  isDirectory: s.isDirectory,
  isFile: !s.isDirectory && !s.isSymbolicLink,
  isSymbolicLink: s.isSymbolicLink,
  mtime: new Date(s.mtimeMs),
  size: s.size,
});

export const createWorkVm = async (
  options: WorkVmOptions
): Promise<{
  readonly destroy: () => Promise<void>;
  readonly sandbox: SandboxFactory;
}> => {
  const vm = await AgentOs.create({
    defaultSoftware: true,
    mounts: [
      {
        path: "/workspace/repository",
        plugin: createHostDirBackend({
          hostPath: options.worktreePath,
          readOnly: false,
        }),
        readOnly: false,
      },
    ],
    software: [...WORK_SOFTWARE, ...(options.extraSoftware ?? [])],
  } satisfies AgentOsOptions);

  const api: SandboxApi = {
    async exec(
      command: string,
      opts?: {
        cwd?: string;
        env?: Record<string, string>;
        signal?: AbortSignal;
        timeoutMs?: number;
      }
    ) {
      const result = await vm.exec(command, {
        ...(opts?.cwd === undefined ? {} : { cwd: opts.cwd }),
        ...(opts?.env === undefined ? {} : { env: opts.env }),
        ...(opts?.timeoutMs === undefined ? {} : { timeout: opts.timeoutMs }),
      });
      return {
        exitCode: result.exitCode,
        stderr: result.stderr,
        stdout: result.stdout,
      };
    },
    async exists(path: string): Promise<boolean> {
      try {
        return await vm.exists(path);
      } catch {
        return false;
      }
    },
    async mkdir(path: string, opts?: { recursive?: boolean }) {
      await vm.mkdir(path, { recursive: opts?.recursive ?? false });
    },
    readFile(path: string): Promise<string> {
      return vm.readFile(path).then((b) => new TextDecoder().decode(b));
    },
    readFileBuffer(path: string) {
      return vm.readFile(path);
    },
    readdir(path: string): Promise<string[]> {
      return vm.readdir(path);
    },
    async rm(path: string, opts?: { force?: boolean; recursive?: boolean }) {
      if (opts?.force && !(await vm.exists(path))) {
        return;
      }
      await vm.remove(path, { recursive: opts?.recursive });
    },
    async stat(path: string): Promise<FileStat> {
      return statResult(await vm.stat(path));
    },
    writeFile(path: string, content: string | Uint8Array): Promise<void> {
      return vm.writeFile(path, content);
    },
  };

  const sandbox: SandboxFactory = {
    createSessionEnv: () =>
      Promise.resolve(createSandboxSessionEnv(api, "/workspace")),
  };

  return {
    destroy: () => Promise.resolve(),
    sandbox,
  };
};
