import { Injectable, Logger, Optional, Inject, forwardRef } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bullmq';
import { EventBusService } from '../../common/event-bus/event-bus.service';
import { BaseDomainEvent } from '../../common/events/base-domain.event';
import { PrismaService } from '../database/prisma.service';
import { Queue } from 'bullmq';
import { defaultJobOptionsFor } from './queue-definitions';

export {
  QUEUE_AI_ANALYSIS,
  QUEUE_DOCUMENT_IMPORT,
  QUEUE_NOTIFICATION,
  QUEUE_DOMAIN_EVENTS,
  QUEUE_AUDIT_LOG,
  QUEUE_FILE_CLEANUP,
  QUEUE_AI_INTERACTIVE,
  QUEUE_AI_BACKGROUND,
} from './queue.constants';
import {
  QUEUE_AI_ANALYSIS,
  QUEUE_DOCUMENT_IMPORT,
  QUEUE_NOTIFICATION,
  QUEUE_DOMAIN_EVENTS,
  QUEUE_AUDIT_LOG,
  QUEUE_FILE_CLEANUP,
  QUEUE_AI_INTERACTIVE,
  QUEUE_AI_BACKGROUND,
} from './queue.constants';

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

  constructor(
    private readonly prisma: PrismaService,
    @InjectQueue(QUEUE_AI_ANALYSIS) private readonly aiQueue: Queue,
    @InjectQueue(QUEUE_DOCUMENT_IMPORT) private readonly importQueue: Queue,
    @InjectQueue(QUEUE_NOTIFICATION) private readonly notifyQueue: Queue,
    @InjectQueue(QUEUE_DOMAIN_EVENTS) private readonly domainEventsQueue: Queue,
    @InjectQueue(QUEUE_AUDIT_LOG) private readonly auditLogQueue: Queue,
    @InjectQueue(QUEUE_FILE_CLEANUP) private readonly fileCleanupQueue: Queue,
    @InjectQueue(QUEUE_AI_INTERACTIVE) private readonly aiInteractiveQueue: Queue,
    @InjectQueue(QUEUE_AI_BACKGROUND) private readonly aiBackgroundQueue: Queue,
    @Optional() private readonly eventBus?: any,
  ) {}

  async add(queueName: string, data: any, opts?: { jobId?: string; attempts?: number; backoff?: number }) {
    const queue = this.getQueue(queueName);
    const jobOpts = defaultJobOptionsFor(queueName);
    const job = await queue.add(queueName, data, {
      attempts: jobOpts.attempts,
      backoff: jobOpts.backoff,
      removeOnComplete: jobOpts.removeOnComplete,
      removeOnFail: jobOpts.removeOnFail,
      ...opts,
    });

    // Log to DB
    await this.prisma.taskLog.create({ data: { queueName, jobId: job.id || '', status: 'enqueued', payload: JSON.parse(JSON.stringify(data)) } }).catch(() => {});

    this.eventBus?.publish(new (class extends BaseDomainEvent { eventType = 'task.enqueued'; queueName: string; jobId: string; constructor(q: string, j: string) { super(); this.queueName = q; this.jobId = j; } })(queueName, job.id || ''));
    this.logger.log(`Job ${job.id} added to ${queueName}`);
    return job;
  }

  async getJob(queueName: string, jobId: string) {
    const queue = this.getQueue(queueName);
    return queue.getJob(jobId);
  }

  private getQueue(name: string): Queue {
    switch (name) {
      case QUEUE_AI_ANALYSIS: return this.aiQueue;
      case QUEUE_DOCUMENT_IMPORT: return this.importQueue;
      case QUEUE_NOTIFICATION: return this.notifyQueue;
      case QUEUE_DOMAIN_EVENTS: return this.domainEventsQueue;
      case QUEUE_AUDIT_LOG: return this.auditLogQueue;
      case QUEUE_FILE_CLEANUP: return this.fileCleanupQueue;
      case QUEUE_AI_INTERACTIVE: return this.aiInteractiveQueue;
      case QUEUE_AI_BACKGROUND: return this.aiBackgroundQueue;
      default: throw new Error(`Unknown queue: ${name}`);
    }
  }
}
