import { Queue, Worker, type Job } from 'bullmq';
import { CampaignEmailStatus } from '@prisma/client';
import { env } from '../config/env';
import { prisma } from '../config/database';
import { logger } from '../utils/logger';
import { emailService } from './email.service';
import { recordEmailSent, recordEmailFailed, setEmailQueueSize, emailSendDuration } from '../utils/metrics';
import { eventBus } from './event-bus.service';

interface CampaignEmailJobData {
  campaignEmailId: string;
  recipientEmail: string;
  recipientName?: string;
  recipientLevel?: string;
  userEmail: string;
  userName: string;
  postalCode: string;
  subject: string;
  message: string;
  campaignTitle: string;
}

class EmailQueueService {
  private queue: Queue;
  private worker: Worker | null = null;

  constructor() {
    this.queue = new Queue('campaign-emails', {
      connection: { url: env.REDIS_URL },
      defaultJobOptions: {
        attempts: 3,
        backoff: { type: 'exponential', delay: 2000 },
        removeOnComplete: { age: 24 * 60 * 60, count: 1000 },
        removeOnFail: { age: 7 * 24 * 60 * 60 },
      },
    });
  }

  startWorker() {
    this.worker = new Worker(
      'campaign-emails',
      async (job: Job<CampaignEmailJobData>) => {
        const { campaignEmailId, ...emailData } = job.data;
        const endTimer = emailSendDuration.startTimer();

        logger.info(`Processing email job ${job.id} for campaign-email ${campaignEmailId}`);

        // Look up campaign ID for metrics
        const emailRecord = await prisma.campaignEmail.findUnique({
          where: { id: campaignEmailId },
          select: { campaignId: true },
        });
        const campaignId = emailRecord?.campaignId || 'unknown';

        const result = await emailService.sendCampaignEmail(emailData);
        endTimer();

        const status = result.success
          ? CampaignEmailStatus.SENT
          : CampaignEmailStatus.FAILED;

        await prisma.campaignEmail.update({
          where: { id: campaignEmailId },
          data: { status },
        });

        if (result.success) {
          recordEmailSent(campaignId);
          // Publish campaign email sent event
          eventBus.publish('campaign.email.sent', {
            email: emailData.userEmail,
            name: emailData.userName,
            campaignSlug: emailData.campaignTitle,
            postalCode: emailData.postalCode,
          });
        } else {
          recordEmailFailed(campaignId, 'send_failure');
          throw new Error(`Failed to send email to ${emailData.recipientEmail}`);
        }

        return { messageId: result.messageId, testMode: result.testMode };
      },
      {
        connection: { url: env.REDIS_URL },
        concurrency: 3,
      }
    );

    this.worker.on('completed', (job) => {
      logger.info(`Email job ${job.id} completed`);
    });

    this.worker.on('failed', (job, err) => {
      logger.error(`Email job ${job?.id} failed: ${err.message}`);
    });

    logger.info('Email queue worker started');
  }

  async addCampaignEmail(data: CampaignEmailJobData): Promise<string> {
    const job = await this.queue.add('campaign', data);
    return job.id!;
  }

  async getStats() {
    const [waiting, active, completed, failed, paused] = await Promise.all([
      this.queue.getWaitingCount(),
      this.queue.getActiveCount(),
      this.queue.getCompletedCount(),
      this.queue.getFailedCount(),
      this.queue.isPaused(),
    ]);
    setEmailQueueSize(waiting + active);
    return { waiting, active, completed, failed, paused };
  }

  async pause() {
    await this.queue.pause();
    logger.info('Email queue paused');
  }

  async resume() {
    await this.queue.resume();
    logger.info('Email queue resumed');
  }

  async clean(grace: number = 24 * 60 * 60 * 1000) {
    const cleaned = await this.queue.clean(grace, 1000, 'completed');
    logger.info(`Cleaned ${cleaned.length} completed jobs`);
    return cleaned.length;
  }

  async close() {
    if (this.worker) {
      await this.worker.close();
    }
    await this.queue.close();
    logger.info('Email queue closed');
  }
}

export const emailQueueService = new EmailQueueService();
