import { Injectable, Inject } from "@nestjs/common";
import { InjectQueue } from "@nestjs/bullmq";
import { Queue, Job } from "bullmq";
import { Logger } from "nestjs-pino";
import type { SendEmailOptions, EmailCategory } from "../email.service.js";
import { QUEUE_NAMES } from "../../queue/queue.constants.js";

export type EmailJobData = SendEmailOptions & {
  category?: EmailCategory;
};

export interface EmailJobResult {
  jobId: string;
  queued: boolean;
}

/** Queue configuration constants */
const QUEUE_CONFIG = {
  /** Keep last N completed jobs for debugging */
  REMOVE_ON_COMPLETE: 100,
  /** Keep last N failed jobs for investigation */
  REMOVE_ON_FAIL: 200,
  /** Max retry attempts */
  MAX_ATTEMPTS: 3,
  /** Initial backoff delay in ms */
  BACKOFF_DELAY: 2000,
} as const;

@Injectable()
export class EmailQueueService {
  constructor(
    @InjectQueue(QUEUE_NAMES.EMAIL) private readonly queue: Queue<EmailJobData>,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  async enqueueEmail(data: EmailJobData): Promise<EmailJobResult> {
    const jobContext = {
      service: "email-queue",
      queue: QUEUE_NAMES.EMAIL,
      category: data.category || "transactional",
      recipientCount: Array.isArray(data.to) ? data.to.length : 1,
      hasTemplate: !!data.templateId,
    };

    try {
      const job = await this.queue.add("send", data, {
        removeOnComplete: QUEUE_CONFIG.REMOVE_ON_COMPLETE,
        removeOnFail: QUEUE_CONFIG.REMOVE_ON_FAIL,
        attempts: QUEUE_CONFIG.MAX_ATTEMPTS,
        backoff: {
          type: "exponential",
          delay: QUEUE_CONFIG.BACKOFF_DELAY,
        },
      });

      this.logger.log("Email job queued", {
        ...jobContext,
        jobId: job.id,
        subject: data.subject,
      });

      return { jobId: job.id || "unknown", queued: true };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);

      this.logger.error("Failed to queue email job", {
        ...jobContext,
        subject: data.subject,
        error: errorMessage,
      });

      throw new Error(`Failed to queue email: ${errorMessage}`);
    }
  }

  /**
   * Get queue health metrics for monitoring
   */
  async getQueueHealth(): Promise<{
    waiting: number;
    active: number;
    completed: number;
    failed: number;
    delayed: number;
  }> {
    const [waiting, active, completed, failed, delayed] = await Promise.all([
      this.queue.getWaitingCount(),
      this.queue.getActiveCount(),
      this.queue.getCompletedCount(),
      this.queue.getFailedCount(),
      this.queue.getDelayedCount(),
    ]);

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

  /**
   * Get failed jobs for debugging/retry
   */
  async getFailedJobs(limit = 10): Promise<Job<EmailJobData>[]> {
    return this.queue.getFailed(0, limit - 1);
  }

  /**
   * Retry a specific failed job
   */
  async retryJob(jobId: string): Promise<void> {
    const job = await this.queue.getJob(jobId);
    if (!job) {
      this.logger.warn("Job not found for retry", {
        service: "email-queue",
        jobId,
      });
      return;
    }

    await job.retry();
    this.logger.log("Job retry triggered", {
      service: "email-queue",
      jobId,
      category: job.data.category,
    });
  }
}
