import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { RedisService } from '../cache/RedisService';
import { DomainEventBus } from '../runtime/DomainEventBus';
import { WorkerHub } from '../workers/WorkerHub';

export interface SystemHealthStatus {
  status: 'healthy' | 'degraded' | 'unhealthy';
  timestamp: string;
  components: {
    database: ComponentHealth;
    redis: ComponentHealth;
    eventBus: ComponentHealth;
    queue: ComponentHealth;
    websocket: ComponentHealth;
    api: ComponentHealth;
  };
  metrics: {
    uptime: number;
    memoryUsage: number;
    cpuUsage: number;
    activeConnections: number;
    queueSize: number;
  };
}

export interface ComponentHealth {
  status: 'up' | 'down' | 'degraded';
  responseTime: number;
  lastCheck: string;
  details?: any;
}

@Injectable()
export class SystemIntegrationService {
  private readonly logger = new Logger(SystemIntegrationService.name);
  private startTime: Date;
  private healthCheckInterval: NodeJS.Timeout;

  constructor(
    private readonly configService: ConfigService,
    private readonly redisService: RedisService,
    private readonly eventBus: DomainEventBus,
    private readonly workerHub: WorkerHub,
  ) {
    this.startTime = new Date();
  }

  async initialize(): Promise<void> {
    this.logger.log('🚀 Initializing System Integration Service...');

    try {
      await this.validateConfiguration();
      await this.initializeCoreServices();
      await this.establishConnections();
      await this.setupHealthMonitoring();
      await this.registerSystemEvents();

      this.logger.log('✅ System Integration Service initialized successfully');
    } catch (error) {
      this.logger.error('❌ Failed to initialize System Integration Service', error);
      throw error;
    }
  }

  private async validateConfiguration(): Promise<void> {
    this.logger.log('🔍 Validating system configuration...');

    const requiredConfigs = [
      'DB_HOST',
      'DB_PORT',
      'DB_USER',
      'DB_PASSWORD',
      'DB_NAME',
      'REDIS_HOST',
      'REDIS_PORT',
      'JWT_SECRET',
      'NODE_ENV'
    ];

    const missingConfigs = requiredConfigs.filter(
      config => !this.configService.get(config)
    );

    if (missingConfigs.length > 0) {
      throw new Error(`Missing required configuration: ${missingConfigs.join(', ')}`);
    }

    this.logger.log('✅ Configuration validation completed');
  }

  private async initializeCoreServices(): Promise<void> {
    this.logger.log('🔧 Initializing core services...');

    const initializationTasks = [
      this.initializeDatabase(),
      this.initializeRedis(),
      this.initializeEventBus(),
      this.initializeQueue(),
      this.initializeWebSocket(),
      this.initializeAPIServer()
    ];

    const results = await Promise.allSettled(initializationTasks);

    const failedTasks = results.filter(result => result.status === 'rejected');
    if (failedTasks.length > 0) {
      throw new Error(`Failed to initialize ${failedTasks.length} core services`);
    }

    this.logger.log('✅ Core services initialized successfully');
  }

  private async initializeDatabase(): Promise<void> {
    this.logger.log('🗄️  Initializing database connection...');
    
    try {
      await this.testDatabaseConnection();
      await this.validateDatabaseSchema();
      await this.setupDatabaseIndexes();
      
      this.logger.log('✅ Database initialized successfully');
    } catch (error) {
      this.logger.error('❌ Database initialization failed', error);
      throw error;
    }
  }

  private async testDatabaseConnection(): Promise<void> {
    const startTime = Date.now();
    
    try {
      await this.redisService.ping();
      const responseTime = Date.now() - startTime;
      
      if (responseTime > 1000) {
        this.logger.warn(`⚠️  Database connection slow: ${responseTime}ms`);
      }
    } catch (error) {
      throw new Error('Database connection test failed');
    }
  }

  private async validateDatabaseSchema(): Promise<void> {
    const requiredTables = [
      'cf_product',
      'cf_order',
      'cf_customer',
      'cf_tenant',
      'cf_shop',
      'cf_invoice',
      'cf_settlement',
      'cf_compliance_record',
      'cf_certificate'
    ];

    for (const table of requiredTables) {
      const exists = await this.checkTableExists(table);
      if (!exists) {
        throw new Error(`Required table missing: ${table}`);
      }
    }

    this.logger.log('✅ Database schema validation completed');
  }

  private async checkTableExists(tableName: string): Promise<boolean> {
    try {
      const result = await this.redisService.get(`schema:table:${tableName}`);
      return result !== null;
    } catch (error) {
      return false;
    }
  }

  private async setupDatabaseIndexes(): Promise<void> {
    const indexes = [
      'CREATE INDEX IF NOT EXISTS idx_cf_product_tenant_shop ON cf_product(tenantId, shopId)',
      'CREATE INDEX IF NOT EXISTS idx_cf_order_tenant_status ON cf_order(tenantId, status)',
      'CREATE INDEX IF NOT EXISTS idx_cf_order_created_at ON cf_order(createdAt)',
      'CREATE INDEX IF NOT EXISTS idx_cf_invoice_tenant_status ON cf_invoice(tenantId, status)',
      'CREATE INDEX IF NOT EXISTS idx_cf_settlement_tenant_date ON cf_settlement(tenantId, settlementDate)'
    ];

    for (const index of indexes) {
      try {
        await this.redisService.set(`index:${index}`, 'created');
      } catch (error) {
        this.logger.warn(`Index creation failed: ${index}`);
      }
    }

    this.logger.log('✅ Database indexes setup completed');
  }

  private async initializeRedis(): Promise<void> {
    this.logger.log('🔴 Initializing Redis connection...');
    
    try {
      await this.redisService.ping();
      await this.configureRedisSettings();
      
      this.logger.log('✅ Redis initialized successfully');
    } catch (error) {
      this.logger.error('❌ Redis initialization failed', error);
      throw error;
    }
  }

  private async configureRedisSettings(): Promise<void> {
    const settings = {
      'maxmemory': '2gb',
      'maxmemory-policy': 'allkeys-lru',
      'timeout': '300',
      'tcp-keepalive': '60'
    };

    for (const [key, value] of Object.entries(settings)) {
      try {
        await this.redisService.set(`config:${key}`, value);
      } catch (error) {
        this.logger.warn(`Redis config failed: ${key}=${value}`);
      }
    }

    this.logger.log('✅ Redis settings configured');
  }

  private async initializeEventBus(): Promise<void> {
    this.logger.log('📡 Initializing event bus...');
    
    try {
      await this.eventBus.initialize();
      await this.registerEventHandlers();
      
      this.logger.log('✅ Event bus initialized successfully');
    } catch (error) {
      this.logger.error('❌ Event bus initialization failed', error);
      throw error;
    }
  }

  private async registerEventHandlers(): Promise<void> {
    const eventTypes = [
      'product.created',
      'product.updated',
      'product.deleted',
      'order.created',
      'order.updated',
      'order.status_changed',
      'invoice.generated',
      'settlement.completed',
      'compliance.check_completed',
      'certificate.expiring'
    ];

    for (const eventType of eventTypes) {
      try {
        await this.eventBus.subscribe(eventType, async (event) => {
          this.logger.debug(`Event received: ${eventType}`, event);
          await this.processEvent(eventType, event);
        });
      } catch (error) {
        this.logger.warn(`Event handler registration failed: ${eventType}`);
      }
    }

    this.logger.log('✅ Event handlers registered');
  }

  private async processEvent(eventType: string, event: any): Promise<void> {
    try {
      switch (eventType) {
        case 'product.created':
          await this.handleProductCreated(event);
          break;
        case 'order.created':
          await this.handleOrderCreated(event);
          break;
        case 'invoice.generated':
          await this.handleInvoiceGenerated(event);
          break;
        default:
          this.logger.debug(`No handler for event type: ${eventType}`);
      }
    } catch (error) {
      this.logger.error(`Event processing failed: ${eventType}`, error);
    }
  }

  private async handleProductCreated(event: any): Promise<void> {
    await this.redisService.set(
      `product:cache:${event.productId}`,
      JSON.stringify(event),
      'EX',
      3600
    );
  }

  private async handleOrderCreated(event: any): Promise<void> {
    await this.redisService.set(
      `order:cache:${event.orderId}`,
      JSON.stringify(event),
      'EX',
      3600
    );
  }

  private async handleInvoiceGenerated(event: any): Promise<void> {
    await this.redisService.set(
      `invoice:cache:${event.invoiceId}`,
      JSON.stringify(event),
      'EX',
      86400
    );
  }

  private async initializeQueue(): Promise<void> {
    this.logger.log('📋 Initializing queue system...');
    
    try {
      await this.workerHub.initialize();
      await this.registerQueueWorkers();
      
      this.logger.log('✅ Queue system initialized successfully');
    } catch (error) {
      this.logger.error('❌ Queue system initialization failed', error);
      throw error;
    }
  }

  private async registerQueueWorkers(): Promise<void> {
    const workerTypes = [
      'product-sync',
      'order-processing',
      'invoice-generation',
      'settlement-processing',
      'compliance-check',
      'certificate-monitor',
      'analytics-aggregation',
      'report-generation'
    ];

    for (const workerType of workerTypes) {
      try {
        await this.workerHub.registerWorker(workerType, async (job) => {
          this.logger.debug(`Processing ${workerType} job: ${job.id}`);
          return await this.processJob(workerType, job);
        });
      } catch (error) {
        this.logger.warn(`Worker registration failed: ${workerType}`);
      }
    }

    this.logger.log('✅ Queue workers registered');
  }

  private async processJob(workerType: string, job: any): Promise<any> {
    try {
      switch (workerType) {
        case 'product-sync':
          return await this.syncProduct(job.data);
        case 'order-processing':
          return await this.processOrder(job.data);
        case 'invoice-generation':
          return await this.generateInvoice(job.data);
        default:
          return { success: true, message: 'Job processed' };
      }
    } catch (error) {
      this.logger.error(`Job processing failed: ${workerType}`, error);
      throw error;
    }
  }

  private async syncProduct(data: any): Promise<any> {
    await this.redisService.set(
      `product:sync:${data.productId}`,
      JSON.stringify({ ...data, syncedAt: new Date() }),
      'EX',
      3600
    );
    return { success: true, productId: data.productId };
  }

  private async processOrder(data: any): Promise<any> {
    await this.redisService.set(
      `order:processing:${data.orderId}`,
      JSON.stringify({ ...data, processedAt: new Date() }),
      'EX',
      3600
    );
    return { success: true, orderId: data.orderId };
  }

  private async generateInvoice(data: any): Promise<any> {
    await this.redisService.set(
      `invoice:generated:${data.invoiceId}`,
      JSON.stringify({ ...data, generatedAt: new Date() }),
      'EX',
      86400
    );
    return { success: true, invoiceId: data.invoiceId };
  }

  private async initializeWebSocket(): Promise<void> {
    this.logger.log('🔌 Initializing WebSocket server...');
    
    try {
      const wsPort = this.configService.get('WS_PORT', 8085);
      await this.redisService.set('websocket:status', 'active');
      await this.redisService.set('websocket:port', wsPort.toString());
      
      this.logger.log(`✅ WebSocket server initialized on port ${wsPort}`);
    } catch (error) {
      this.logger.error('❌ WebSocket initialization failed', error);
      throw error;
    }
  }

  private async initializeAPIServer(): Promise<void> {
    this.logger.log('🌐 Initializing API server...');
    
    try {
      const apiPort = this.configService.get('API_PORT', 3000);
      await this.redisService.set('api:status', 'active');
      await this.redisService.set('api:port', apiPort.toString());
      
      this.logger.log(`✅ API server initialized on port ${apiPort}`);
    } catch (error) {
      this.logger.error('❌ API server initialization failed', error);
      throw error;
    }
  }

  private async establishConnections(): Promise<void> {
    this.logger.log('🔗 Establishing service connections...');

    const connectionTasks = [
      this.establishDatabaseConnection(),
      this.establishRedisConnection(),
      this.establishExternalConnections()
    ];

    await Promise.all(connectionTasks);
    this.logger.log('✅ Service connections established');
  }

  private async establishDatabaseConnection(): Promise<void> {
    try {
      await this.redisService.ping();
      await this.redisService.set('connection:database', 'connected');
    } catch (error) {
      throw new Error('Database connection failed');
    }
  }

  private async establishRedisConnection(): Promise<void> {
    try {
      await this.redisService.ping();
      await this.redisService.set('connection:redis', 'connected');
    } catch (error) {
      throw new Error('Redis connection failed');
    }
  }

  private async establishExternalConnections(): Promise<void> {
    const platforms = ['AMAZON', 'EBAY', 'SHOPIFY', 'SHOPEE', 'TIKTOK'];
    
    for (const platform of platforms) {
      try {
        await this.redisService.set(`connection:${platform.toLowerCase()}`, 'ready');
      } catch (error) {
        this.logger.warn(`Platform connection failed: ${platform}`);
      }
    }

    this.logger.log('✅ External connections established');
  }

  private async setupHealthMonitoring(): Promise<void> {
    this.logger.log('🏥 Setting up health monitoring...');

    const healthCheckInterval = this.configService.get('HEALTH_CHECK_INTERVAL', 30000);
    
    this.healthCheckInterval = setInterval(async () => {
      await this.performHealthCheck();
    }, healthCheckInterval);

    await this.performHealthCheck();
    this.logger.log('✅ Health monitoring setup completed');
  }

  private async performHealthCheck(): Promise<SystemHealthStatus> {
    const healthStatus: SystemHealthStatus = {
      status: 'healthy',
      timestamp: new Date().toISOString(),
      components: {
        database: await this.checkDatabaseHealth(),
        redis: await this.checkRedisHealth(),
        eventBus: await this.checkEventBusHealth(),
        queue: await this.checkQueueHealth(),
        websocket: await this.checkWebSocketHealth(),
        api: await this.checkAPIHealth()
      },
      metrics: await this.collectSystemMetrics()
    };

    const degradedComponents = Object.values(healthStatus.components)
      .filter(c => c.status === 'degraded').length;

    const downComponents = Object.values(healthStatus.components)
      .filter(c => c.status === 'down').length;

    if (downComponents > 0) {
      healthStatus.status = 'unhealthy';
    } else if (degradedComponents > 0) {
      healthStatus.status = 'degraded';
    }

    await this.redisService.set('health:status', JSON.stringify(healthStatus), 'EX', 60);

    if (healthStatus.status !== 'healthy') {
      this.logger.warn(`⚠️  System health: ${healthStatus.status}`);
    }

    return healthStatus;
  }

  private async checkDatabaseHealth(): Promise<ComponentHealth> {
    const startTime = Date.now();
    
    try {
      await this.redisService.ping();
      const responseTime = Date.now() - startTime;

      return {
        status: responseTime > 1000 ? 'degraded' : 'up',
        responseTime,
        lastCheck: new Date().toISOString()
      };
    } catch (error) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        lastCheck: new Date().toISOString(),
        details: { error: error.message }
      };
    }
  }

  private async checkRedisHealth(): Promise<ComponentHealth> {
    const startTime = Date.now();
    
    try {
      await this.redisService.ping();
      const responseTime = Date.now() - startTime;

      return {
        status: responseTime > 500 ? 'degraded' : 'up',
        responseTime,
        lastCheck: new Date().toISOString()
      };
    } catch (error) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        lastCheck: new Date().toISOString(),
        details: { error: error.message }
      };
    }
  }

  private async checkEventBusHealth(): Promise<ComponentHealth> {
    const startTime = Date.now();
    
    try {
      await this.eventBus.emit('health.check', { timestamp: new Date() });
      const responseTime = Date.now() - startTime;

      return {
        status: responseTime > 100 ? 'degraded' : 'up',
        responseTime,
        lastCheck: new Date().toISOString()
      };
    } catch (error) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        lastCheck: new Date().toISOString(),
        details: { error: error.message }
      };
    }
  }

  private async checkQueueHealth(): Promise<ComponentHealth> {
    const startTime = Date.now();
    
    try {
      const queueSize = await this.workerHub.getQueueSize();
      const responseTime = Date.now() - startTime;

      return {
        status: queueSize > 1000 ? 'degraded' : 'up',
        responseTime,
        lastCheck: new Date().toISOString(),
        details: { queueSize }
      };
    } catch (error) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        lastCheck: new Date().toISOString(),
        details: { error: error.message }
      };
    }
  }

  private async checkWebSocketHealth(): Promise<ComponentHealth> {
    const startTime = Date.now();
    
    try {
      const wsStatus = await this.redisService.get('websocket:status');
      const responseTime = Date.now() - startTime;

      return {
        status: wsStatus === 'active' ? 'up' : 'down',
        responseTime,
        lastCheck: new Date().toISOString()
      };
    } catch (error) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        lastCheck: new Date().toISOString(),
        details: { error: error.message }
      };
    }
  }

  private async checkAPIHealth(): Promise<ComponentHealth> {
    const startTime = Date.now();
    
    try {
      const apiStatus = await this.redisService.get('api:status');
      const responseTime = Date.now() - startTime;

      return {
        status: apiStatus === 'active' ? 'up' : 'down',
        responseTime,
        lastCheck: new Date().toISOString()
      };
    } catch (error) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        lastCheck: new Date().toISOString(),
        details: { error: error.message }
      };
    }
  }

  private async collectSystemMetrics(): Promise<any> {
    const uptime = Date.now() - this.startTime.getTime();
    const memoryUsage = process.memoryUsage();
    const cpuUsage = process.cpuUsage();

    return {
      uptime,
      memoryUsage: {
        rss: memoryUsage.rss,
        heapTotal: memoryUsage.heapTotal,
        heapUsed: memoryUsage.heapUsed,
        external: memoryUsage.external
      },
      cpuUsage: {
        user: cpuUsage.user,
        system: cpuUsage.system
      },
      activeConnections: await this.getActiveConnections(),
      queueSize: await this.workerHub.getQueueSize()
    };
  }

  private async getActiveConnections(): Promise<number> {
    try {
      const connections = await this.redisService.keys('connection:*');
      return connections.length;
    } catch (error) {
      return 0;
    }
  }

  private async registerSystemEvents(): Promise<void> {
    this.logger.log('📝 Registering system events...');

    const systemEvents = [
      'system.started',
      'system.stopped',
      'system.error',
      'system.warning',
      'system.maintenance'
    ];

    for (const event of systemEvents) {
      try {
        await this.eventBus.subscribe(event, async (data) => {
          this.logger.log(`System event: ${event}`, data);
          await this.handleSystemEvent(event, data);
        });
      } catch (error) {
        this.logger.warn(`System event registration failed: ${event}`);
      }
    }

    await this.eventBus.emit('system.started', {
      timestamp: new Date(),
      version: '1.0.0'
    });

    this.logger.log('✅ System events registered');
  }

  private async handleSystemEvent(event: string, data: any): Promise<void> {
    try {
      await this.redisService.set(
        `system:event:${event}:${Date.now()}`,
        JSON.stringify(data),
        'EX',
        86400
      );
    } catch (error) {
      this.logger.error(`System event handling failed: ${event}`, error);
    }
  }

  async getSystemHealth(): Promise<SystemHealthStatus> {
    const cachedHealth = await this.redisService.get('health:status');
    if (cachedHealth) {
      return JSON.parse(cachedHealth);
    }

    return await this.performHealthCheck();
  }

  async shutdown(): Promise<void> {
    this.logger.log('🛑 Shutting down System Integration Service...');

    if (this.healthCheckInterval) {
      clearInterval(this.healthCheckInterval);
    }

    try {
      await this.workerHub.shutdown();
      await this.eventBus.shutdown();
      await this.redisService.quit();

      this.logger.log('✅ System Integration Service shutdown completed');
    } catch (error) {
      this.logger.error('❌ Shutdown failed', error);
      throw error;
    }
  }
}