import { Injectable, Logger } from "@nestjs/common";
import { InjectQueue } from "@nestjs/bullmq";
import { Queue } from "bullmq";

export interface EmbeddingJobData {
  entryId: string;
  content: string;
  model?: string;
}

/**
 * Service for managing the embedding generation queue
 *
 * This service provides an interface to queue embedding jobs
 * and manage the queue lifecycle.
 */
@Injectable()
export class EmbeddingQueueService {
  private readonly logger = new Logger(EmbeddingQueueService.name);

  constructor(
    @InjectQueue("embeddings")
    private readonly embeddingQueue: Queue<EmbeddingJobData>
  ) {}

  /**
   * Queue an embedding generation job
   *
   * @param entryId - ID of the knowledge entry
   * @param content - Content to generate embedding for
   * @param model - Optional model override
   * @returns Job ID
   */
  async queueEmbeddingJob(entryId: string, content: string, model?: string): Promise<string> {
    const jobData: EmbeddingJobData = {
      entryId,
      content,
    };

    if (model !== undefined) {
      jobData.model = model;
    }

    const job = await this.embeddingQueue.add("generate-embedding", jobData, {
      // Retry configuration
      attempts: 3,
      backoff: {
        type: "exponential",
        delay: 5000, // Start with 5 seconds
      },
      // Rate limiting: 1 job per second to avoid overwhelming Ollama
      delay: 1000,
      // Remove completed jobs after 24 hours
      removeOnComplete: {
        age: 86400, // 24 hours in seconds
        count: 1000, // Keep max 1000 completed jobs
      },
      // Remove failed jobs after 7 days
      removeOnFail: {
        age: 604800, // 7 days in seconds
        count: 100, // Keep max 100 failed jobs for debugging
      },
    });

    this.logger.log(`Queued embedding job ${job.id ?? "unknown"} for entry ${entryId}`);
    return job.id ?? "unknown";
  }

  /**
   * Get queue statistics
   *
   * @returns Queue job counts
   */
  async getQueueStats(): Promise<{
    waiting: number;
    active: number;
    completed: number;
    failed: number;
  }> {
    const counts = await this.embeddingQueue.getJobCounts(
      "waiting",
      "active",
      "completed",
      "failed"
    );

    return {
      waiting: counts.waiting ?? 0,
      active: counts.active ?? 0,
      completed: counts.completed ?? 0,
      failed: counts.failed ?? 0,
    };
  }

  /**
   * Clean completed jobs older than the grace period
   *
   * @param gracePeriodMs - Grace period in milliseconds (default: 24 hours)
   */
  async cleanCompletedJobs(gracePeriodMs = 86400000): Promise<void> {
    await this.embeddingQueue.clean(gracePeriodMs, 100, "completed");
    this.logger.log(`Cleaned completed jobs older than ${gracePeriodMs.toString()}ms`);
  }

  /**
   * Clean failed jobs older than the grace period
   *
   * @param gracePeriodMs - Grace period in milliseconds (default: 7 days)
   */
  async cleanFailedJobs(gracePeriodMs = 604800000): Promise<void> {
    await this.embeddingQueue.clean(gracePeriodMs, 100, "failed");
    this.logger.log(`Cleaned failed jobs older than ${gracePeriodMs.toString()}ms`);
  }
}
