import Fastify, { type FastifyInstance, type FastifyServerOptions } from "fastify";
import websocket from "@fastify/websocket";
import { resolveNetwork, GLOBAL_NETWORK_ID } from "./auth.js";
import { createOpencodeProbe } from "./opencode.js";
import gatewayModule from "./modules/trapos-cloud-gateway/index.js";
import { GatewayRegistry } from "./modules/trapos-cloud-gateway/registry.js";
import mcpModule from "./modules/mcp/index.js";

export type Config = {
  host: string;
  port: number;
  serverPassword?: string;
  requestTimeoutMs: number;
  opencodeUrl?: string;
  opencodeUsername: string;
  opencodeServerPassword?: string;
  // MCP HTTP endpoint (exec-lua / write-file / probe-computers). Bound to mcpHost,
  // which defaults to 127.0.0.1 so the powerful tools stay local-only.
  mcpEnabled: boolean;
  mcpHost: string;
  mcpPort: number;
  mcpDefaultTimeoutMs: number;
  mcpMaxTimeoutMs: number;
  mcpProbeTimeoutMs: number;
  logger: FastifyServerOptions["logger"];
};

const MAX_PAYLOAD_BYTES = 1024 * 1024; // ~1MB

// Composes the service: registry + opencode probe + auth seam, wired into the
// trapos-cloud-gateway module, plus an optional MCP app sharing the same registry.
// Never reads process.env (that is index.ts's job), so it is fully constructable
// from tests. The MCP app is a separate Fastify instance (its own host/port) so it
// can bind to localhost independently of the public gateway listener.
export async function createApp(
  config: Config,
): Promise<{ app: FastifyInstance; mcpApp: FastifyInstance | null; registry: GatewayRegistry }> {
  const app = Fastify({ logger: config.logger, disableRequestLogging: true });

  const registry = new GatewayRegistry(app.log, config.requestTimeoutMs);
  const probe = createOpencodeProbe({
    opencodeUrl: config.opencodeUrl,
    opencodeUsername: config.opencodeUsername,
    opencodeServerPassword: config.opencodeServerPassword,
  });

  await app.register(websocket, { options: { maxPayload: MAX_PAYLOAD_BYTES } });
  await app.register(gatewayModule, {
    registry,
    probe,
    resolveNetwork: (secret) => resolveNetwork(secret, config.serverPassword),
  });

  const mcpApp = config.mcpEnabled ? await createMcpApp(config, registry) : null;

  return { app, mcpApp, registry };
}

async function createMcpApp(config: Config, registry: GatewayRegistry): Promise<FastifyInstance> {
  const mcpApp = Fastify({ logger: config.logger, disableRequestLogging: true });
  await mcpApp.register(mcpModule, {
    registry,
    networkId: GLOBAL_NETWORK_ID,
    defaultTimeoutMs: config.mcpDefaultTimeoutMs,
    maxTimeoutMs: config.mcpMaxTimeoutMs,
    probeTimeoutMs: config.mcpProbeTimeoutMs,
  });
  return mcpApp;
}
