import { InjectQueue } from '@nestjs/bullmq';
import { Injectable, Logger } from '@nestjs/common';
import type { Queue } from 'bullmq';
import type { SendVerificationEmailJob } from './types/email-job.interface';
import type { MiddlewareEmpresaJob } from './types/middleware-empresa-job.interface';
import type {
  SendFacturaToSifenJob,
  SendNotaCreditoToSifenJob,
} from './types/sifen-job.interface';

@Injectable()
export class QueuesService {
  private readonly logger = new Logger(QueuesService.name);

  constructor(
    @InjectQueue('email-queue')
    private readonly emailQueue: Queue<SendVerificationEmailJob>,
    @InjectQueue('sifen-queue')
    private readonly sifenQueue: Queue<SendFacturaToSifenJob>,
    @InjectQueue('sifen-nc-queue')
    private readonly sifenNCQueue: Queue<SendNotaCreditoToSifenJob>,
    @InjectQueue('middleware-empresa-queue')
    private readonly middlewareEmpresaQueue: Queue<MiddlewareEmpresaJob>,
  ) {}

  /**
   * Agrega un job para enviar código de verificación por email.
   *
   * ⚠️ SEGURIDAD: hoy el único caller está comentado (envío OTP directo vía
   * NotificacionesService). Si se reactiva esta ruta, NO encolar `code` en texto
   * plano — Bull Board (/queues) muestra la data de los jobs (los fallidos quedan
   * con `removeOnFail:false`). Encolar solo `{ codigoId, email }` y que el processor
   * lea/genere el código desde DB. El panel /queues ya está detrás de basic-auth
   * (ver main.ts), pero esto es defensa en profundidad.
   */
  async sendVerificationEmail(
    email: string,
    code: string,
    userId: string,
  ): Promise<void> {
    try {
      await this.emailQueue.add(
        'send-verification-code',
        {
          email,
          code,
          userId,
        },
        {
          attempts: 3, // Reintentar hasta 3 veces
          backoff: {
            type: 'exponential',
            delay: 2000, // Esperar 2s, luego 4s, luego 8s
          },
          removeOnComplete: true, // Eliminar jobs completados
          removeOnFail: false, // Mantener jobs fallidos para debugging
        },
      );

      this.logger.log(`Email job queued for ${email}`);
    } catch (error) {
      this.logger.error(`Failed to queue email job for ${email}:`, error);
      throw error;
    }
  }

  /**
   * Obtiene estadísticas de la cola de emails
   */
  async getEmailQueueStats() {
    const [waiting, active, completed, failed] = await Promise.all([
      this.emailQueue.getWaitingCount(),
      this.emailQueue.getActiveCount(),
      this.emailQueue.getCompletedCount(),
      this.emailQueue.getFailedCount(),
    ]);

    return {
      waiting,
      active,
      completed,
      failed,
    };
  }

  /**
   * Limpia jobs completados y fallidos
   */
  async cleanEmailQueue() {
    await this.emailQueue.clean(0, 1000, 'completed');
    await this.emailQueue.clean(0, 1000, 'failed');
    this.logger.log('Email queue cleaned');
  }

  async enqueueSifenFactura(
    facturaId: string,
    empresaId: string,
  ): Promise<void> {
    try {
      await this.sifenQueue.add(
        'send-factura-to-middleware',
        {
          facturaId,
          empresaId,
        },
        {
          attempts: 5,
          backoff: {
            type: 'exponential',
            delay: 5000,
          },
          removeOnComplete: { count: 100 },
          removeOnFail: false,
        },
      );

      this.logger.log(
        `Middleware SIFEN job queued for factura ${facturaId} - empresa ${empresaId}`,
      );
    } catch (error) {
      this.logger.error(
        `Failed to queue Middleware SIFEN job for factura ${facturaId} - empresa ${empresaId}:`,
        error,
      );
      throw error;
    }
  }

  /**
   * Encola una nota de crédito para envío a SIFEN
   */
  async enqueueSifenNotaCredito(
    notaCreditoId: string,
    empresaId: string,
  ): Promise<void> {
    try {
      await this.sifenNCQueue.add(
        'send-nc-to-middleware',
        {
          notaCreditoId,
          empresaId,
        },
        {
          attempts: 5,
          backoff: {
            type: 'exponential',
            delay: 5000,
          },
          removeOnComplete: { count: 100 },
          removeOnFail: false,
        },
      );

      this.logger.log(
        `Middleware SIFEN NC job queued for nota crédito ${notaCreditoId} - empresa ${empresaId}`,
      );
    } catch (error) {
      this.logger.error(
        `Failed to queue Middleware SIFEN NC job for nota crédito ${notaCreditoId}:`,
        error,
      );
      throw error;
    }
  }

  /**
   * Encola sincronización de empresa al middleware panel
   * @param empresaId ID de la empresa
   * @param operacion A=Alta, M=Modificar, B=Baja
   * @param origen Módulo que dispara (empresas, sucursales, timbrados)
   */
  async enqueueMiddlewareEmpresa(
    empresaId: string,
    operacion: 'A' | 'M' | 'B',
    origen: string,
  ): Promise<void> {
    try {
      await this.middlewareEmpresaQueue.add(
        'sync-empresa-to-middleware',
        {
          empresaId,
          operacion,
          origen,
        },
        {
          attempts: 3,
          backoff: {
            type: 'exponential',
            delay: 5000,
          },
          removeOnComplete: { count: 100 },
          removeOnFail: false,
        },
      );

      this.logger.log(
        `Middleware empresa job queued: empresa=${empresaId} op=${operacion} origen=${origen}`,
      );
    } catch (error) {
      this.logger.error(
        `Failed to queue middleware empresa job for ${empresaId}:`,
        error,
      );
      // No lanzar error para no bloquear la operación principal
    }
  }
}
