import Fastify from 'fastify';
import websocket from '@fastify/websocket';
import cors from '@fastify/cors';
import { UserService } from './db/user-service.js';
import { Authenticator } from './auth/authenticator.js';
import { WebSocketHandler } from './channels/websocket-handler.js';
import { TelegramHandler } from './channels/telegram-handler.js';
import { KubernetesClient } from './k8s/client.js';
import { ContainerManager } from './k8s/container-manager.js';

const app = Fastify({
  logger: {
    level: process.env.LOG_LEVEL || 'info',
    transport: {
      target: 'pino-pretty',
      options: {
        colorize: true,
        translateTime: 'HH:MM:ss Z',
        ignore: 'pid,hostname',
      },
    },
  },
});

// Configuration from environment
const config = {
  port: parseInt(process.env.PORT || '3000'),
  host: process.env.HOST || '0.0.0.0',
  databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost/dexorder',

  // LLM provider API keys
  providerConfig: {
    anthropicApiKey: process.env.ANTHROPIC_API_KEY,
    openaiApiKey: process.env.OPENAI_API_KEY,
    googleApiKey: process.env.GOOGLE_API_KEY,
    openrouterApiKey: process.env.OPENROUTER_API_KEY,
  },

  telegramBotToken: process.env.TELEGRAM_BOT_TOKEN || '',

  // Kubernetes configuration
  kubernetes: {
    namespace: process.env.KUBERNETES_NAMESPACE || 'dexorder-agents',
    inCluster: process.env.KUBERNETES_IN_CLUSTER === 'true',
    context: process.env.KUBERNETES_CONTEXT,
    agentImage: process.env.AGENT_IMAGE || 'ghcr.io/dexorder/agent:latest',
    sidecarImage: process.env.SIDECAR_IMAGE || 'ghcr.io/dexorder/lifecycle-sidecar:latest',
    storageClass: process.env.AGENT_STORAGE_CLASS || 'standard',
  },
};

// Validate at least one LLM provider is configured
const hasAnyProvider = Object.values(config.providerConfig).some(key => !!key);
if (!hasAnyProvider) {
  app.log.error('At least one LLM provider API key is required (ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, or OPENROUTER_API_KEY)');
  process.exit(1);
}

// Register plugins
await app.register(cors, {
  origin: process.env.CORS_ORIGIN || '*',
});

await app.register(websocket, {
  options: {
    maxPayload: 1024 * 1024, // 1MB
  },
});

// Initialize services
const userService = new UserService(config.databaseUrl);

// Initialize Kubernetes client and container manager
const k8sClient = new KubernetesClient({
  namespace: config.kubernetes.namespace,
  inCluster: config.kubernetes.inCluster,
  context: config.kubernetes.context,
  logger: app.log,
});

const containerManager = new ContainerManager({
  k8sClient,
  agentImage: config.kubernetes.agentImage,
  sidecarImage: config.kubernetes.sidecarImage,
  storageClass: config.kubernetes.storageClass,
  namespace: config.kubernetes.namespace,
  logger: app.log,
});

const authenticator = new Authenticator({
  userService,
  containerManager,
  logger: app.log,
});

// Initialize channel handlers
const websocketHandler = new WebSocketHandler({
  authenticator,
  providerConfig: config.providerConfig,
});

const telegramHandler = new TelegramHandler({
  authenticator,
  providerConfig: config.providerConfig,
  telegramBotToken: config.telegramBotToken,
});

// Register routes
websocketHandler.register(app);
telegramHandler.register(app);

// Health check
app.get('/health', async () => {
  return {
    status: 'ok',
    timestamp: new Date().toISOString(),
  };
});

// Graceful shutdown
const shutdown = async () => {
  app.log.info('Shutting down gracefully...');
  try {
    await userService.close();
    await app.close();
    app.log.info('Shutdown complete');
    process.exit(0);
  } catch (error) {
    app.log.error({ error }, 'Error during shutdown');
    process.exit(1);
  }
};

process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);

// Start server
try {
  await app.listen({
    port: config.port,
    host: config.host,
  });

  app.log.info(
    {
      port: config.port,
      host: config.host,
    },
    'Gateway server started'
  );
} catch (error) {
  app.log.error({ error }, 'Failed to start server');
  process.exit(1);
}
