/* eslint-disable max-classes-per-file -- the AgentOS service and its tagged error form one adapter contract. */
import pi from "@agentos-software/pi";
import { agentOS as createAgentOsActor } from "@rivet-dev/agentos";
import type { AgentOSConfigInput } from "@rivet-dev/agentos";
import { Context, Effect, Layer, Schema } from "effect";

import { getExecutableGitSoftware } from "./agent-os-git-software";

const withGitSoftware = (
  config: AgentOSConfigInput<undefined>
): AgentOSConfigInput<undefined> => ({
  ...config,
  software: [getExecutableGitSoftware(), ...(config.software ?? [])],
});

export class AgentOsError extends Schema.TaggedErrorClass<AgentOsError>()(
  "AgentOsError",
  {
    cause: Schema.Defect(),
  }
) {}

export interface AgentOsOptions {
  readonly config?: AgentOSConfigInput<undefined>;
}

export class AgentOs extends Context.Service<
  AgentOs,
  {
    readonly createActor: (
      config?: AgentOSConfigInput<undefined>
    ) => Effect.Effect<
      ReturnType<typeof createAgentOsActor<undefined>>,
      AgentOsError
    >;
  }
>()("@code/primitives/agent-os/AgentOs") {
  static readonly layer = Layer.succeed(
    AgentOs,
    AgentOs.of({
      createActor: Effect.fn("AgentOs.createActor")(function* createActor(
        config: AgentOSConfigInput<undefined> = {}
      ) {
        return yield* Effect.try({
          catch: (cause) => new AgentOsError({ cause }),
          try: () => createAgentOsActor<undefined>(withGitSoftware(config)),
        });
      }),
    })
  );
}

export const makeAgentOsLayer = (options: AgentOsOptions = {}) =>
  Layer.succeed(
    AgentOs,
    AgentOs.of({
      createActor: Effect.fn("AgentOs.createActor")(function* createActor(
        config: AgentOSConfigInput<undefined> = options.config ?? {}
      ) {
        return yield* Effect.try({
          catch: (cause) => new AgentOsError({ cause }),
          try: () => createAgentOsActor<undefined>(withGitSoftware(config)),
        });
      }),
    })
  );

export const createAgentOsActorEffect = Effect.fn("createAgentOsActorEffect")(
  function* createAgentOsActorEffect(config?: AgentOSConfigInput<undefined>) {
    const agentOs = yield* AgentOs;
    return yield* agentOs.createActor(config);
  }
);

// ---------------------------------------------------------------------------
// Pi harness configuration
//
// Slice 5 intentionally runs one harness against the server's fixed Zopu
// checkout. Pi speaks ACP natively and reads its model gateway from a mounted
// HOME, avoiding a second executable adapter or credentials path.
// ---------------------------------------------------------------------------

/** Software bundle for a Pi-capable VM: the Pi ACP harness plus git. */
export const piSoftware = [pi, getExecutableGitSoftware()] as const;

export interface PiModelConfig {
  readonly api: "openai-completions";
  readonly apiKeyEnvironmentVariable: string;
  readonly baseUrl: string;
  readonly contextWindow: number;
  readonly maxTokens: number;
  readonly model: string;
  readonly provider: string;
}

export interface PiHomeFiles {
  readonly models: string;
  readonly settings: string;
}

/** Builds the two files Pi reads from ~/.pi/agent. */
export const makePiHomeFiles = (model: PiModelConfig): PiHomeFiles => ({
  models: JSON.stringify(
    {
      providers: {
        [model.provider]: {
          api: model.api,
          apiKey: model.apiKeyEnvironmentVariable,
          authHeader: true,
          baseUrl: model.baseUrl,
          models: [
            {
              contextWindow: model.contextWindow,
              id: model.model,
              maxTokens: model.maxTokens,
              name: model.model,
            },
          ],
        },
      },
    },
    null,
    2
  ),
  settings: JSON.stringify(
    {
      defaultModel: model.model,
      defaultProvider: model.provider,
      quietStartup: true,
    },
    null,
    2
  ),
});

/** Environment supplied to the Pi ACP session. */
export const piSessionEnv = (
  apiKey: string,
  extra: Readonly<Record<string, string>> = {}
): Record<string, string> => ({
  AGENT_MODEL_API_KEY: apiKey,
  HOME: "/home/zopu",
  PATH: "/opt/zopu-tools/bin:/usr/local/bin:/usr/bin:/bin",
  ...extra,
});

export interface PiAgentOsConfigInput {
  /** Extra software merged after the Pi+git bundle. */
  readonly software?: NonNullable<AgentOSConfigInput<undefined>["software"]>;
  /** VM permission overrides merged over the execution policy. */
  readonly permissions?: AgentOSConfigInput<undefined>["permissions"];
}

export const makePiAgentOsConfig = (
  input: PiAgentOsConfigInput = {}
): AgentOSConfigInput<undefined> => ({
  permissions: {
    childProcess: "allow",
    env: "allow",
    fs: "allow",
    network: "allow",
    process: "allow",
    ...input.permissions,
  },
  software: [...piSoftware, ...(input.software ?? [])],
});
