import Fastify from 'fastify';
import { config } from './lib/config.js';
import { logger } from './lib/logger.js';
import { initializeContainers } from './lib/cosmos-init.js';
import { extractAuth, AuthError, requireAdmin } from './lib/auth.js';
import { generateCsrfToken, validateCsrfToken, getSessionId } from './lib/csrf.js';
import { serviceRoutes } from './modules/services/routes.js';
import { deploymentRoutes } from './modules/deployments/routes.js';
import { healthRoutes } from './modules/health/routes.js';
import { auditRoutes } from './modules/audit/routes.js';
import { backupRoutes } from './modules/backup/routes.js';
import { systemRoutes } from './modules/system/routes.js';
import { envRoutes } from './modules/env/routes.js';
import { azureConfigRoutes } from './modules/azure-config/routes.js';
import { codeQualityRoutes } from './modules/code-quality/routes.js';
import { cosmosConfigRoutes } from './modules/cosmos-config/routes.js';
import { hermesOpsRoutes } from './modules/hermes-ops/routes.js';
import { hermesTelemetryRoutes } from './modules/hermes-telemetry/routes.js';
import { vmRoutes } from './modules/vm/routes.js';
import rateLimit from '@fastify/rate-limit';
import swagger from '@fastify/swagger';
import swaggerUi from '@fastify/swagger-ui';

// Hand Fastify the shared pino instance so its per-request logs go through
// the same formatter + redaction config as everything else in the backend.
const fastify = Fastify({
  loggerInstance: logger,
});

// NOTE: there is no Server-Sent-Events log stream. `fastify-sse-v2 ^4` is not
// compatible with Fastify 5 and was never functional here. The deployment-log
// endpoint (`GET /api/deployments/:id/logs`) returns JSON; the web client
// polls it. If a real-time stream is wanted later, ship it explicitly via
// `reply.raw` or a Fastify-5-compatible plugin and update the docs in the
// same change.

// Register rate limiting
await fastify.register(rateLimit, {
  max: 100, // 100 requests per window
  timeWindow: '1 minute',
  errorResponseBuilder: (request, context) => ({
    code: 429,
    error: 'Too many requests',
    retryAfter: context.ttl,
  }),
  skipOnError: false,
});

// Register Swagger
await fastify.register(swagger, {
  openapi: {
    openapi: '3.0.0',
    info: {
      title: 'ByteLyst DevOps API',
      description: 'API for deployment orchestration and service monitoring',
      version: '0.1.0',
    },
    servers: [
      {
        url: 'http://localhost:4004',
        description: 'Development server',
      },
    ],
    components: {
      securitySchemes: {
        bearerAuth: {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
        },
      },
    },
  },
});

await fastify.register(swaggerUi, {
  routePrefix: '/docs',
  uiConfig: {
    docExpansion: 'list',
    deepLinking: false,
  },
  staticCSP: true,
  transformStaticCSP: (header) => header,
  transformSpecification: (swaggerObject, request, reply) => {
    return swaggerObject;
  },
  transformSpecificationClone: true,
});

// Auth hook - extract user info from JWT
fastify.addHook('onRequest', async (request, reply) => {
  const auth = await extractAuth(request);
  if (auth) {
    (request as any).authUserId = auth.userId;
    (request as any).authRole = auth.role;
    (request as any).authEmail = auth.email;
    (request as any).authProductId = auth.productId;
  }
});

// CSRF protection hook - validate CSRF tokens for state-changing requests
fastify.addHook('onRequest', async (request, reply) => {
  const method = request.method;
  const stateChangingMethods = ['POST', 'PUT', 'DELETE', 'PATCH'];

  if (!stateChangingMethods.includes(method)) {
    return;
  }

  const sessionId = getSessionId(request);
  if (!sessionId) {
    return reply.code(401).send({ error: 'Unauthorized' });
  }

  const csrfToken = request.headers['x-csrf-token'] as string;
  if (!csrfToken) {
    return reply.code(403).send({ error: 'CSRF token missing' });
  }

  if (!validateCsrfToken(csrfToken, sessionId)) {
    return reply.code(403).send({ error: 'Invalid CSRF token' });
  }
});

// Performance monitoring hook
fastify.addHook('onRequest', async (request, reply) => {
  (request as any).startTime = Date.now();
});

fastify.addHook('onResponse', async (request, reply) => {
  const startTime = (request as any).startTime;
  if (startTime) {
    const duration = Date.now() - startTime;
    const route = request.routeOptions.url || request.url;
    const method = request.method;

    fastify.log.info({
      method,
      route,
      statusCode: reply.statusCode,
      duration,
      performance: 'slow',
    }, `${method} ${route} - ${reply.statusCode} (${duration}ms)`);

    // Alert on slow responses (> 1 second)
    if (duration > 1000) {
      fastify.log.warn({
        method,
        route,
        statusCode: reply.statusCode,
        duration,
        alert: 'slow-response',
      }, `Slow response detected: ${method} ${route} took ${duration}ms`);
    }
  }
});

// Error handler for AuthError
fastify.setErrorHandler((error, request, reply) => {
  if (error instanceof AuthError) {
    return reply.code(error.statusCode).send({ error: error.message });
  }

  // Default error handling
  reply.code(500).send({ error: 'Internal server error' });
});

// CORS - more secure configuration.
//
// Built-in defaults cover localhost dev + the production domain. Add extra
// origins (comma-separated) via the `EXTRA_CORS_ORIGINS` env var — e.g. a
// Tailscale MagicDNS hostname for private internal access — so the allow-list
// can grow without a rebuild. A literal `*` is honoured for parity with the
// preflight handler, but only set that in trusted networks.
const EXTRA_CORS_ORIGINS = (process.env.EXTRA_CORS_ORIGINS ?? '')
  .split(',')
  .map((o) => o.trim())
  .filter(Boolean);
fastify.addHook('onSend', async (request, reply) => {
  const allowedOrigins = [
    'http://localhost:3000',
    'http://localhost:3001',
    'https://devops.bytelyst.com',
    ...EXTRA_CORS_ORIGINS,
  ];

  const origin = request.headers.origin;
  if (origin && (allowedOrigins.includes(origin) || allowedOrigins.includes('*'))) {
    reply.header('Access-Control-Allow-Origin', origin);
    reply.header('Vary', 'Origin');
  }

  reply.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
  reply.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  reply.header('Access-Control-Allow-Credentials', 'true');
  reply.header('Access-Control-Max-Age', '86400'); // 24 hours

  // Security headers
  reply.header('X-Content-Type-Options', 'nosniff');
  reply.header('X-Frame-Options', 'DENY');
  reply.header('X-XSS-Protection', '1; mode=block');
  reply.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  reply.header('Referrer-Policy', 'strict-origin-when-cross-origin');
});

// Handle OPTIONS preflight requests
fastify.options('*', async (request, reply) => {
  reply.code(204).send();
});

// Health check
fastify.get('/health', async () => ({ status: 'ok', service: 'devops-backend' }));

// Register standalone routes with /api prefix
await fastify.register(async function (fastify) {
  // Performance metrics endpoint (admin only) - DEPRECATED: Use /api/system/metrics instead
  fastify.get('/metrics', {
    preHandler: async (req) => requireAdmin(req),
  }, async (request, reply) => {
    try {
      const { getSystemMetrics } = await import('./modules/system/repository.js');
      const metrics = await getSystemMetrics();
      return reply.send(metrics);
    } catch (error) {
      fastify.log.error(error, 'Failed to get metrics');
      return reply.code(500).send({ error: 'Failed to get metrics' });
    }
  });

  // CSRF token endpoint
  fastify.get('/csrf-token', async (request, reply) => {
    const sessionId = getSessionId(request);
    if (!sessionId) {
      return reply.code(401).send({ error: 'Unauthorized' });
    }

    const token = generateCsrfToken(sessionId);
    return { csrfToken: token };
  });

  // Seed default services
  fastify.post('/seed', async (request, reply) => {
    const { createService } = await import('./modules/services/repository.js');

    const defaultServices = [
      {
        id: 'trading',
        name: 'Investment Trading',
        scriptPath: '../deploy-invttrdg.sh',
        healthUrl: 'https://api.bytelyst.com/invttrdg/health',
        repoPath: '../learning_ai_invt_trdg',
      },
      {
        id: 'notes',
        name: 'Agentic Notes',
        scriptPath: '../deploy-notes.sh',
        healthUrl: 'https://api.notelett.app/health',
        repoPath: '../learning_ai_notes',
      },
      {
        id: 'clock',
        name: 'AI Clock',
        scriptPath: '../deploy-clock.sh',
        healthUrl: 'https://api.clock.bytelyst.com/health',
        repoPath: '../learning_ai_clock',
      },
    ];

    for (const serviceData of defaultServices) {
      try {
        await createService(serviceData);
      } catch (error) {
        fastify.log.info({ serviceId: serviceData.id }, 'Service might already exist');
      }
    }

    return reply.send({ message: 'Seeded default services' });
  });
}, { prefix: '/api' });

// Register modular routes with /api prefix
await fastify.register(serviceRoutes, { prefix: '/api' });
await fastify.register(deploymentRoutes, { prefix: '/api' });
await fastify.register(healthRoutes, { prefix: '/api' });
await fastify.register(auditRoutes, { prefix: '/api' });
await fastify.register(backupRoutes, { prefix: '/api' });
await fastify.register(systemRoutes, { prefix: '/api' });
await fastify.register(envRoutes, { prefix: '/api' });
await fastify.register(azureConfigRoutes, { prefix: '/api' });
await fastify.register(codeQualityRoutes, { prefix: '/api' });
await fastify.register(cosmosConfigRoutes, { prefix: '/api' });
await fastify.register(hermesOpsRoutes, { prefix: '/api' });
await fastify.register(hermesTelemetryRoutes, { prefix: '/api' });
await fastify.register(vmRoutes, { prefix: '/api' });

// Start server
async function start() {
  try {
    // Try to initialize Cosmos containers, but allow server to start even if it fails
    try {
      await initializeContainers();
      fastify.log.info('Cosmos containers initialized successfully');
    } catch (err) {
      fastify.log.warn(err, 'Failed to initialize Cosmos containers (server will start anyway)');
    }

    await fastify.listen({ port: parseInt(config.PORT), host: '0.0.0.0' });
    fastify.log.info({ port: config.PORT }, 'DevOps backend listening');
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
}

start();
