/**
 * HTTP Server - Handles HTTP JSON-RPC requests
 */

import express, { Request, Response } from 'express';
import { RpcHandler, JsonRpcRequest, JsonRpcResponse } from '../handlers/rpc-handler';
import { BesuClient } from '../clients/besu-client';
import { Web3SignerClient } from '../clients/web3signer-client';
import { VaultClient } from '../clients/vault-client';
import Redis from 'ioredis';
import { v4 as uuidv4 } from 'uuid';

export class HttpServer {
  private app: express.Application;
  private server: any;
  private port: number;
  private rpcHandler: RpcHandler;
  private besuClient: BesuClient;
  private web3SignerClient?: Web3SignerClient;
  private vaultClient?: VaultClient;
  private redis?: Redis;

  constructor(
    port: number,
    rpcHandler: RpcHandler,
    besuClient: BesuClient,
    web3SignerClient?: Web3SignerClient,
    vaultClient?: VaultClient,
    redis?: Redis
  ) {
    this.port = port;
    this.rpcHandler = rpcHandler;
    this.besuClient = besuClient;
    this.web3SignerClient = web3SignerClient;
    this.vaultClient = vaultClient;
    this.redis = redis;
    this.app = express();
    this.setupMiddleware();
    this.setupRoutes();
  }

  private setupMiddleware() {
    // Request logging middleware with request ID
    this.app.use((req: Request, res: Response, next: express.NextFunction) => {
      const requestId = uuidv4();
      (req as any).requestId = requestId;
      
      // Log request
      console.log(`[${requestId}] ${req.method} ${req.path}`);
      
      // Add request ID to response headers
      res.setHeader('X-Request-ID', requestId);
      next();
    });

    // Body parser
    this.app.use(express.json({ limit: '10mb' }));

    // CORS (can be configured more restrictively in production)
    this.app.use((req: Request, res: Response, next: express.NextFunction) => {
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
      if (req.method === 'OPTIONS') {
        return res.sendStatus(200);
      }
      next();
    });
  }

  private setupRoutes() {
    // Health check endpoint
    this.app.get('/health', async (req: Request, res: Response) => {
      try {
        const healthStatus = this.besuClient.getHealthStatus();
        
        // Check Redis connectivity
        let redisHealthy = false;
        let redisError: string | undefined;
        if (this.redis) {
          try {
            await this.redis.ping();
            redisHealthy = true;
          } catch (error: any) {
            redisError = error.message;
          }
        }
        
        // Check Web3Signer connectivity
        let web3SignerHealthy = false;
        let web3SignerError: string | undefined;
        if (this.web3SignerClient) {
          try {
            const response = await (this.web3SignerClient as any).client.get('/upcheck', { timeout: 3000 });
            web3SignerHealthy = response.status === 200 && response.data === 'OK';
          } catch (error: any) {
            web3SignerError = error.message;
          }
        }
        
        // Check Vault connectivity
        let vaultHealthy = false;
        let vaultError: string | undefined;
        if (this.vaultClient) {
          try {
            // Check if Vault client has a token (means it's initialized)
            vaultHealthy = !!(this.vaultClient as any).token;
            if (!vaultHealthy) {
              vaultError = 'Vault not initialized';
            }
          } catch (error: any) {
            vaultError = error.message;
          }
        }
        
        const besuHealthy = healthStatus.healthyCount > 0;
        const overallHealthy = besuHealthy && redisHealthy && (this.web3SignerClient ? web3SignerHealthy : true) && (this.vaultClient ? vaultHealthy : true);
        
        res.json({
          status: overallHealthy ? 'ok' : 'degraded',
          service: 'rpc-translator-138',
          timestamp: new Date().toISOString(),
          components: {
            besu: {
              healthy: besuHealthy,
              healthyCount: healthStatus.healthyCount,
              totalCount: healthStatus.totalCount,
              nodes: healthStatus.nodes.map(node => ({
                httpUrl: node.httpUrl,
                healthy: node.healthy,
                lastHealthCheck: node.lastHealthCheck,
              })),
            },
            redis: {
              healthy: redisHealthy,
              error: redisError,
            },
            web3signer: this.web3SignerClient ? {
              healthy: web3SignerHealthy,
              error: web3SignerError,
            } : undefined,
            vault: this.vaultClient ? {
              healthy: vaultHealthy,
              error: vaultError,
            } : undefined,
          },
        });
      } catch (error: any) {
        res.status(500).json({
          status: 'error',
          error: error.message,
          timestamp: new Date().toISOString(),
        });
      }
    });

    // JSON-RPC endpoint
    this.app.post('/', async (req: Request, res: Response) => {
      const requestId = (req as any).requestId || uuidv4();

      try {
        const body = req.body;

        // Handle batch requests
        if (Array.isArray(body)) {
          const requests: JsonRpcRequest[] = body;
          const responses = await this.rpcHandler.handleBatch(requests);
          return res.json(responses);
        }

        // Handle single request
        const request: JsonRpcRequest = body;
        const response = await this.rpcHandler.handleRequest(request);

        // Log response
        if (response.error) {
          console.log(`[${requestId}] Error: ${response.error.message} (code: ${response.error.code})`);
        } else {
          console.log(`[${requestId}] Success: ${request.method}`);
        }

        return res.json(response);
      } catch (error: any) {
        console.error(`[${requestId}] Unexpected error:`, error);
        const response: JsonRpcResponse = {
          jsonrpc: '2.0',
          error: {
            code: -32603,
            message: 'Internal error',
            data: error.message,
          },
          id: null,
        };
        return res.status(500).json(response);
      }
    });

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

  async start(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.server = this.app.listen(this.port, () => {
        console.log(`HTTP server started on port ${this.port}`);
        resolve();
      });

      this.server.on('error', (error: any) => {
        if (error.code === 'EADDRINUSE') {
          reject(new Error(`Port ${this.port} is already in use`));
        } else {
          reject(error);
        }
      });
    });
  }

  async stop(): Promise<void> {
    return new Promise((resolve) => {
      if (this.server) {
        this.server.close(() => {
          console.log('HTTP server stopped');
          resolve();
        });
      } else {
        resolve();
      }
    });
  }
}
