import express, { Request, Response } from 'express';
import http from 'http';
import cors from 'cors';
import helmet from 'helmet';
import compression from 'compression';
import { config, validateConfig } from './config';
import logger from './utils/logger';
import { getMetrics } from './utils/metrics';
import { initializeBrowserPool } from './routes/proxy.routes';
import { initializeDemoUser } from './middleware/auth';
import { connectDB } from './db/connection';
import { StreamingServer } from './websocket/StreamingServer';
import authRoutes from './routes/auth.routes';
import proxyRoutes from './routes/proxy.routes';
import supportRoutes from './routes/support.routes';
import searchesRoutes from './routes/searches.routes';

/**
 * Main application class
 */
class Application {
  private app: express.Application;
  private server: http.Server;
  private streamingServer: StreamingServer | null = null;
  private browserPool: any;

  constructor() {
    this.app = express();
    this.server = http.createServer(this.app);
  }

  /**
   * Initialize the application
   */
  async initialize(): Promise<void> {
    try {
      // Validate configuration
      validateConfig();

      // Setup middleware
      this.setupMiddleware();

      // Setup routes
      this.setupRoutes();

      // Initialize browser pool
      logger.info('Initializing browser pool...');
      this.browserPool = initializeBrowserPool();

      // Initialize WebSocket streaming server
      logger.info('Initializing WebSocket streaming server...');
      this.streamingServer = new StreamingServer(this.server, this.browserPool);

      // Connect to MongoDB
      await connectDB();

      // Initialize demo user for testing
      await initializeDemoUser();

      logger.info('Application initialized successfully');
    } catch (error) {
      logger.error('Failed to initialize application', {
        error: error instanceof Error ? error.message : String(error),
      });
      throw error;
    }
  }

  /**
   * Setup Express middleware
   */
  private setupMiddleware(): void {
    // Security headers
    this.app.use(helmet({
      contentSecurityPolicy: false, // Disable for proxy functionality
    }));

    // CORS — explicit allowlist (supports credentials)
    this.app.use(cors({
      origin: (origin, callback) => {
        // allow server-to-server / curl / same-origin requests (no Origin header)
        if (!origin) return callback(null, true);
        if (config.allowedOrigins.includes(origin)) return callback(null, true);
        callback(new Error(`CORS: origin ${origin} not allowed`));
      },
      credentials: true,
    }));

    // Compression
    this.app.use(compression());

    // Body parsing
    this.app.use(express.json({ limit: '10mb' }));
    this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));

    // Request logging
    this.app.use((req, _res, next) => {
      logger.debug('Incoming request', {
        method: req.method,
        path: req.path,
        ip: req.ip,
      });
      next();
    });

    logger.info('Middleware configured');
  }

  /**
   * Setup API routes
   */
  private setupRoutes(): void {
    // Health check endpoint
    this.app.get('/health', (_req: Request, res: Response) => {
      res.json({
        status: 'healthy',
        timestamp: new Date().toISOString(),
        uptime: process.uptime(),
      });
    });

    // Metrics endpoint (Prometheus)
    if (config.metrics.enabled) {
      this.app.get('/metrics', async (_req: Request, res: Response) => {
        try {
          const metrics = await getMetrics();
          res.set('Content-Type', 'text/plain');
          res.send(metrics);
        } catch (error) {
          logger.error('Failed to get metrics', {
            error: error instanceof Error ? error.message : String(error),
          });
          res.status(500).send('Failed to get metrics');
        }
      });
    }

    // API routes
    this.app.use('/api/auth', authRoutes);
    this.app.use('/api/proxy', proxyRoutes);
    this.app.use('/api/support', supportRoutes);
    this.app.use('/api/searches', searchesRoutes);

    // 404 handler
    this.app.use((req: Request, res: Response) => {
      res.status(404).json({
        error: 'Not found',
        path: req.path,
      });
    });

    // Error handler
    this.app.use((err: Error, req: Request, res: Response, _next: any) => {
      logger.error('Unhandled error', {
        error: err.message,
        stack: err.stack,
        path: req.path,
      });

      res.status(500).json({
        error: config.server.nodeEnv === 'production'
          ? 'Internal server error'
          : err.message,
      });
    });

    logger.info('Routes configured');
  }

  /**
   * Start the application server
   */
  async start(): Promise<void> {
    return new Promise((resolve) => {
      this.server.listen(config.server.port, config.server.host, () => {
        logger.info('Server started', {
          host: config.server.host,
          port: config.server.port,
          nodeEnv: config.server.nodeEnv,
        });

        console.log(`
╔════════════════════════════════════════════════════════════╗
║                                                            ║
║   🚀 Playwright Web Proxy Server                          ║
║                                                            ║
║   HTTP Server:    http://${config.server.host}:${config.server.port}              ║
║   WebSocket:      ws://${config.server.host}:${config.server.port}/ws            ║
║   Health Check:   http://${config.server.host}:${config.server.port}/health      ║
║   Metrics:        http://${config.server.host}:${config.server.port}/metrics     ║
║                                                            ║
║   Demo Credentials:                                        ║
║   Email:          demo@example.com                         ║
║   Password:       demo123                                  ║
║                                                            ║
╚════════════════════════════════════════════════════════════╝
        `);

        resolve();
      });
    });
  }

  /**
   * Graceful shutdown
   */
  async shutdown(): Promise<void> {
    logger.info('Shutting down application...');

    try {
      // Close WebSocket server
      if (this.streamingServer) {
        await this.streamingServer.shutdown();
      }

      // Close browser pool
      if (this.browserPool) {
        await this.browserPool.shutdown();
      }

      // Close HTTP server
      await new Promise<void>((resolve, reject) => {
        this.server.close((err) => {
          if (err) reject(err);
          else resolve();
        });
      });

      logger.info('Application shutdown complete');
      process.exit(0);
    } catch (error) {
      logger.error('Error during shutdown', {
        error: error instanceof Error ? error.message : String(error),
      });
      process.exit(1);
    }
  }
}

/**
 * Main entry point
 */
async function main() {
  const app = new Application();

  // Handle shutdown signals
  process.on('SIGTERM', () => app.shutdown());
  process.on('SIGINT', () => app.shutdown());

  // Handle uncaught errors
  process.on('uncaughtException', (error) => {
    logger.error('Uncaught exception', {
      error: error.message,
      stack: error.stack,
    });
    app.shutdown();
  });

  process.on('unhandledRejection', (reason) => {
    logger.error('Unhandled rejection', {
      reason: String(reason),
    });
    app.shutdown();
  });

  // Initialize and start
  await app.initialize();
  await app.start();
}

// Run the application
if (require.main === module) {
  main().catch((error) => {
    console.error('Failed to start application:', error);
    process.exit(1);
  });
}

export default Application;
