import express, { Express, Request, Response, NextFunction } from 'express';
import cors from 'cors';
import compression from 'compression';
import { apiRateLimiter, strictRateLimiter } from './middleware/rate-limit';
import tokenRoutes from './routes/tokens';
import reportRoutes from './routes/report';
import adminRoutes from './routes/admin';
import configRoutes from './routes/config';
import bridgeRoutes from './routes/bridge';
import quoteRoutes from './routes/quote';
import routeTreeRoutes from './routes/routes';
import tokenMappingRoutes from './routes/token-mapping';
import heatmapRoutes from './routes/heatmap';
import arbitrageRoutes from './routes/arbitrage';
import aggregatorRouteMatrixRoutes from './routes/aggregator-routes';
import partnerPayloadRoutes from './routes/partner-payloads';
import plannerV2Routes from './routes/planner-v2';
import { MultiChainIndexer } from '../indexer/chain-indexer';
import { getDatabasePool } from '../database/client';
import winston from 'winston';

// Setup logger
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      ),
    }),
  ],
});

export class ApiServer {
  private app: Express;
  private port: number;
  private indexerEnabled: boolean;
  private indexer: MultiChainIndexer | null;

  private resolveTrustProxySetting(): boolean | number | string {
    const raw = (process.env.EXPRESS_TRUST_PROXY ?? process.env.TRUST_PROXY ?? '1').trim();
    const normalized = raw.toLowerCase();

    if (normalized === 'true') return true;
    if (normalized === 'false') return false;
    if (/^\d+$/.test(raw)) return parseInt(raw, 10);
    return raw;
  }

  constructor() {
    this.app = express();
    this.port = parseInt(process.env.PORT || '3000', 10);
    this.indexerEnabled = this.resolveFeatureFlag('ENABLE_INDEXER', true);
    this.indexer = this.indexerEnabled ? new MultiChainIndexer() : null;

    this.setupMiddleware();
    this.setupRoutes();
    this.setupErrorHandling();
  }

  private resolveFeatureFlag(name: string, fallback: boolean): boolean {
    const raw = (process.env[name] || '').trim().toLowerCase();
    if (!raw) return fallback;
    if (['1', 'true', 'yes', 'on'].includes(raw)) return true;
    if (['0', 'false', 'no', 'off'].includes(raw)) return false;
    return fallback;
  }

  private setupMiddleware(): void {
    const trustProxy = this.resolveTrustProxySetting();
    this.app.set('trust proxy', trustProxy);

    // CORS
    this.app.use(cors());

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

    // Body parsing
    this.app.use(express.json());
    this.app.use(express.urlencoded({ extended: true }));

    // Rate limiting
    this.app.use('/api/v1', apiRateLimiter);

    // Request logging
    this.app.use((req: Request, res: Response, next: NextFunction) => {
      logger.info(`${req.method} ${req.path}`, {
        ip: req.ip,
        forwardedFor: req.get('x-forwarded-for'),
        trustProxy,
        userAgent: req.get('user-agent'),
      });
      next();
    });
  }

  private setupRoutes(): void {
    // Health check
    this.app.get('/health', async (req: Request, res: Response) => {
      try {
        // Check database connection
        const pool = getDatabasePool();
        await pool.query('SELECT 1');

        res.json({
          status: 'healthy',
          timestamp: new Date().toISOString(),
          services: {
            database: 'connected',
            indexer: this.indexerEnabled ? 'running' : 'disabled',
          },
        });
      } catch (error) {
        res.status(503).json({
          status: 'unhealthy',
          timestamp: new Date().toISOString(),
          error: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    });

    // API routes
    this.app.use('/api/v1', tokenRoutes);
    this.app.use('/api/v1', configRoutes);
    this.app.use('/api/v1/report', reportRoutes);
    this.app.use('/api/v1/bridge', bridgeRoutes);
    this.app.use('/api/v1', routeTreeRoutes);
    this.app.use('/api/v1/token-mapping', tokenMappingRoutes);
    this.app.use('/api/v1', quoteRoutes);
    this.app.use('/api/v1', heatmapRoutes);
    this.app.use('/api/v1', arbitrageRoutes);
    this.app.use('/api/v1', aggregatorRouteMatrixRoutes);
    this.app.use('/api/v1', partnerPayloadRoutes);
    this.app.use('/api/v2', plannerV2Routes);

    // Admin routes (stricter rate limit)
    this.app.use('/api/v1/admin', strictRateLimiter, adminRoutes);

    // Root
    this.app.get('/', (req: Request, res: Response) => {
      res.json({
        name: 'Token Aggregation Service',
        version: '1.0.0',
        endpoints: {
          health: '/health',
          api: '/api/v1',
          apiV2: '/api/v2',
        },
      });
    });
  }

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

    // Error handler
    // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Express error handler requires 4-arg signature
    this.app.use((err: Error, req: Request, res: Response, _next: NextFunction) => {
      logger.error('Unhandled error:', err);
      res.status(500).json({
        error: 'Internal server error',
        message: process.env.NODE_ENV === 'development' ? err.message : undefined,
      });
    });
  }

  async start(): Promise<void> {
    try {
      if (this.indexer) {
        await this.indexer.initialize();
        await this.indexer.startAll();
      } else {
        logger.info('Token aggregation indexer disabled by ENABLE_INDEXER flag');
      }

      // Start server
      this.app.listen(this.port, () => {
        logger.info(`Token Aggregation Service listening on port ${this.port}`);
        logger.info(`Health check: http://localhost:${this.port}/health`);
        logger.info(`API: http://localhost:${this.port}/api/v1`);
      });
    } catch (error) {
      logger.error('Failed to start server:', error);
      process.exit(1);
    }
  }

  async stop(): Promise<void> {
    this.indexer?.stopAll();
    logger.info('Server stopped');
  }
}
