import { randomUUID } from "node:crypto";
import { createAgentSession, type AgentSession, type CreateAgentSessionOptions } from "../agent/index.js";
import type { SessionInfo } from "../session/index.js";
import { listProjectSessions } from "../session/index.js";
import { InProcessGateway } from "./client/InProcessGateway.js";
import { SessionRouter, type GatewaySessionFactory, type SessionRouterOptions } from "./SessionRouter.js";
import type { Gateway, GatewayCronController, GatewayServerInfo } from "./protocol/types.js";

export type GatewayProjectStorageOptions = {
  projectRoot: string;
  pilotHome: string;
};

export type CreateGatewayOptions = {
  session?: {
    create?: GatewaySessionFactory;
    list?: SessionRouterOptions["listSessions"];
  };
  agent?: Omit<CreateAgentSessionOptions, "sessionId" | "projectStorage">;
  projectStorage?: GatewayProjectStorageOptions;
  idleSessionTimeoutMs?: number;
  now?: () => Date;
  uuid?: () => string;
  serverInfo?: Partial<GatewayServerInfo>;
  cron?: GatewayCronController;
};

export function createGateway(options: CreateGatewayOptions): Gateway {
  const now = options.now ?? (() => new Date());
  const uuid = options.uuid ?? randomUUID;
  const createSession = options.session?.create ?? createDefaultSessionFactory(options);
  const listSessions = options.session?.list ?? createDefaultSessionLister(options);
  const router = new SessionRouter({
    createSession,
    listSessions,
    idleSessionTimeoutMs: options.idleSessionTimeoutMs,
    now,
  });

  return new InProcessGateway(router, {
    now,
    uuid,
    serverInfo: {
      mode: "in_process",
      projectKey: options.projectStorage?.projectRoot,
      ...options.serverInfo,
    },
    cron: options.cron,
  });
}

function createDefaultSessionFactory(options: CreateGatewayOptions): GatewaySessionFactory {
  return async ({ sessionKey }) => {
    if (!options.agent) {
      throw new Error("createGateway requires either session.create or agent options.");
    }

    return createAgentSession({
      ...options.agent,
      sessionId: sessionKey,
      projectStorage: options.projectStorage,
    });
  };
}

function createDefaultSessionLister(options: CreateGatewayOptions): SessionRouterOptions["listSessions"] {
  if (!options.projectStorage) {
    return async () => ({ sessions: [] });
  }

  return async ({ limit, cursor }) => {
    const offset = cursor ? Number.parseInt(cursor, 10) : 0;
    const sessions = await listProjectSessions({
      ...options.projectStorage!,
      limit,
      offset: Number.isFinite(offset) ? offset : 0,
    });
    const nextOffset = (Number.isFinite(offset) ? offset : 0) + sessions.length;
    return {
      sessions: sessions.map(toGatewaySessionInfo),
      nextCursor: limit && sessions.length === limit ? String(nextOffset) : undefined,
    };
  };
}

function toGatewaySessionInfo(session: SessionInfo): SessionInfo {
  return session;
}

export type { Gateway, GatewayServerInfo };
export { InProcessGateway, SessionRouter };