import {
  Injectable,
  Logger,
  OnModuleInit,
  OnModuleDestroy,
} from '@nestjs/common';
import { Kafka, Producer, ProducerRecord } from 'kafkajs';
import { KafkaConfig } from './kafka.config';

/**
 * Kafka Producer Service - publishes messages to Kafka cluster.
 * Uses idempotent producer for exactly-once delivery semantics.
 * Supports multi-broker clusters for distributed deployment.
 */
@Injectable()
export class KafkaProducerService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger('KafkaProducer');
  private kafka: Kafka;
  private producer: Producer;
  private isConnected = false;

  constructor(private readonly config: KafkaConfig) {
    this.kafka = new Kafka({
      clientId: config.clientId,
      brokers: config.brokers,
      ssl: config.ssl ? true : undefined,
      sasl: config.sasl,
      retry: {
        retries: config.retries || 5,
        maxRetryTime: config.maxRetryTime || 30000,
      },
    });

    this.producer = this.kafka.producer({
      idempotent: config.idempotent !== false,
      maxInFlightRequests: config.idempotent !== false ? 5 : undefined,
    });
  }

  async onModuleInit() {
    try {
      await this.producer.connect();
      this.isConnected = true;
      this.logger.log(
        `Kafka producer connected to [${this.config.brokers.join(', ')}]`,
      );
    } catch (error) {
      this.logger.error(`Failed to connect producer: ${error.message}`);
      throw error;
    }
  }

  async onModuleDestroy() {
    if (this.isConnected) {
      await this.producer.disconnect();
      this.logger.log('Kafka producer disconnected');
    }
  }

  /**
   * Send a message to a Kafka topic.
   */
  async send(record: ProducerRecord): Promise<void> {
    if (!this.isConnected) {
      throw new Error('Kafka producer is not connected');
    }
    await this.producer.send(record);
  }

  /**
   * Send a domain event with standard headers.
   */
  async sendEvent(
    topic: string,
    key: string,
    eventType: string,
    payload: Record<string, any>,
    headers?: Record<string, string>,
  ): Promise<void> {
    await this.send({
      topic,
      messages: [
        {
          key,
          value: JSON.stringify(payload),
          headers: {
            eventType,
            timestamp: new Date().toISOString(),
            source: this.config.clientId,
            ...headers,
          },
        },
      ],
    });
  }
}
