/**
 * Processor Registry Service
 * 
 * Registry that maps queue types to their corresponding processor services.
 * This allows the SQS poller to dispatch messages to the correct processor
 * based on the message type.
 */

import { Injectable } from '@nestjs/common';
import { QueueType } from '../constants/queue.constants';
import { QueueMessage, QueueProcessingResult } from '../interfaces/queue-message.interface';
import { AppLoggerService } from 'src/common/services/logger.service';

/**
 * Interface that all queue processors must implement
 */
export interface QueueProcessor {
  /**
   * Process a queue message
   * @param message - The message to process
   * @returns Processing result
   */
  process(message: QueueMessage): Promise<QueueProcessingResult>;
}

@Injectable()
export class ProcessorRegistryService {
  // Processors are registered by subType (email, whatsapp, sms, invoice), not queueType
  private readonly processors = new Map<string, QueueProcessor>();

  constructor(private readonly logger: AppLoggerService) {}

  /**
   * Register a processor for a specific message subType
   * 
   * @param subType - The message subType this processor handles (e.g., 'email', 'whatsapp', 'invoice')
   * @param processor - The processor service instance
   */
  registerProcessor(subType: QueueType | string, processor: QueueProcessor): void {
    if (this.processors.has(subType)) {
      this.logger.warn(`Processor for subType '${subType}' is being overwritten`);
    }

    this.processors.set(subType, processor);
    this.logger.log(`Registered processor for subType: ${subType}`);
  }

  /**
   * Get a processor for a specific message subType
   * 
   * @param subType - The message subType
   * @returns The processor or undefined if not found
   */
  getProcessor(subType: string): QueueProcessor | undefined {
    return this.processors.get(subType);
  }

  /**
   * Process a message by dispatching it to the appropriate processor
   * Uses message.subType to determine which processor to use
   * 
   * @param message - The message to process
   * @returns Processing result
   */
  async processMessage(message: QueueMessage): Promise<QueueProcessingResult> {
    const startTime = Date.now();
    const queueType = message.queueType;
    const subType = message.subType;

    try {
      // Look up processor by subType (e.g., 'email', 'whatsapp', 'invoice')
      const processor = this.getProcessor(subType);

      if (!processor) {
        const error = `No processor registered for message subType: '${subType}' (queue: ${queueType})`;
        this.logger.error(error, '', { queueType, subType });
        
        return {
          success: false,
          messageId: message.messageId || 'unknown',
          queueType,
          processingTimeMs: Date.now() - startTime,
          error: {
            code: 'NO_PROCESSOR',
            message: error,
            retryable: false, // Don't retry if processor doesn't exist
          },
        };
      }

      this.logger.debug(
        `Processing message: ${queueType}/${subType}, attempt: ${message.attemptCount || 1}`
      );

      // Call the processor
      const result = await processor.process(message);

      if (result.success) {
        this.logger.debug(
          `Successfully processed ${queueType}/${subType} message in ${result.processingTimeMs}ms`
        );
      } else {
        this.logger.warn(
          `Failed to process ${queueType}/${subType} message: ${result.error?.message}`
        );
      }

      return result;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      const errorStack = error instanceof Error ? error.stack : undefined;

      this.logger.error(
        `Unexpected error processing ${queueType}/${subType} message: ${errorMessage}`,
        '',
        { queueType, subType, error: errorMessage, stack: errorStack },
      );

      return {
        success: false,
        messageId: message.messageId || 'unknown',
        queueType,
        processingTimeMs: Date.now() - startTime,
        error: {
          code: 'PROCESSING_ERROR',
          message: errorMessage,
          stack: errorStack,
          retryable: true, // Retry on unexpected errors
        },
      };
    }
  }

  /**
   * Check if a processor is registered for a specific subType
   * 
   * @param subType - The message subType to check
   * @returns True if processor is registered
   */
  hasProcessor(subType: string): boolean {
    return this.processors.has(subType);
  }

  /**
   * Get all registered subTypes
   * 
   * @returns Array of registered subTypes
   */
  getRegisteredQueueTypes(): string[] {
    return Array.from(this.processors.keys());
  }

  /**
   * Remove a processor (useful for testing or dynamic reconfiguration)
   * 
   * @param subType - The subType to unregister
   * @returns True if processor was removed
   */
  unregisterProcessor(subType: string): boolean {
    const deleted = this.processors.delete(subType);
    if (deleted) {
      this.logger.log(`Unregistered processor for subType: ${subType}`);
    }
    return deleted;
  }
}
