import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis';

export interface MetricsUpdateMessage {
  type: 'metrics_updated';
  timestamp: Date;
  metrics: {
    metricType: string;
    granularity: string;
    value: number;
    dimension?: string;
    dimensionValue?: string;
  }[];
}

/**
 * Redis publisher for broadcasting realtime analytics updates.
 * Publishes aggregation events to the realtime service.
 */
@Injectable()
export class RedisPublisherService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(RedisPublisherService.name);
  private publisher: Redis;
  private readonly CHANNEL = 'analytics:realtime:updates';
  private isConnected = false;

  constructor(private readonly config: ConfigService) {}

  async onModuleInit() {
    const host = this.config.get('REDIS_HOST', 'localhost');
    const port = this.config.get('REDIS_PORT', 6379);

    this.publisher = new Redis({
      host,
      port,
      retryStrategy: (times) => {
        const delay = Math.min(times * 50, 2000);
        return delay;
      },
      maxRetriesPerRequest: 3,
    });

    this.publisher.on('connect', () => {
      this.logger.log(`Redis publisher connected at ${host}:${port}`);
      this.isConnected = true;
    });

    this.publisher.on('error', (error) => {
      this.logger.error(`Redis publisher error: ${error.message}`);
      this.isConnected = false;
    });

    this.publisher.on('close', () => {
      this.logger.warn('Redis publisher connection closed');
      this.isConnected = false;
    });

    // Wait for connection
    await new Promise<void>((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Redis connection timeout'));
      }, 5000);

      this.publisher.once('connect', () => {
        clearTimeout(timeout);
        resolve();
      });

      this.publisher.once('error', (error) => {
        clearTimeout(timeout);
        reject(error);
      });
    });
  }

  async onModuleDestroy() {
    if (this.publisher) {
      await this.publisher.quit();
      this.logger.log('Redis publisher disconnected');
    }
  }

  /**
   * Publish metrics update to realtime channel.
   * Non-blocking - logs errors but doesn't throw.
   */
  async publishUpdate(message: MetricsUpdateMessage): Promise<void> {
    if (!this.isConnected) {
      this.logger.warn('Redis not connected, skipping publish');
      return;
    }

    try {
      const payload = JSON.stringify({
        ...message,
        timestamp: message.timestamp.toISOString(),
      });

      await this.publisher.publish(this.CHANNEL, payload);
      this.logger.debug(`Published update to ${this.CHANNEL}`);
    } catch (error) {
      this.logger.error(`Failed to publish update: ${error}`);
    }
  }

  /**
   * Check if publisher is connected.
   */
  isHealthy(): boolean {
    return this.isConnected && this.publisher?.status === 'ready';
  }
}
