const WhatsAppQueue = require('../../models/WhatsAppQueue');
const eventBus = require('./EventBus');
const Events = require('./CoreEvents');
const { logInfo, logWarn, logError } = require('./LogContext');

class QueueDispatcher {
  constructor() {
    this.activeTenants = new Set();
    this.senders = new Map();
    this.defaultBatchSize = 10;
  }

  registerSender(channel, sender) {
    if (!channel || !sender || typeof sender.send !== 'function') {
      throw new Error('Invalid sender registration');
    }
    this.senders.set(channel, sender);
  }

  async dispatchTenant(tenantId, options = {}) {
    if (this.activeTenants.has(tenantId)) {
      return;
    }
    this.activeTenants.add(tenantId);
    try {
      const batchSize = options.batchSize || this.defaultBatchSize;
      let pending = await WhatsAppQueue.getPending(tenantId, batchSize);
      while (pending.length > 0) {
        for (const item of pending) {
          await this.processItem(tenantId, item);
        }
        pending = await WhatsAppQueue.getPending(tenantId, batchSize);
      }
    } finally {
      this.activeTenants.delete(tenantId);
    }
  }

  async processItem(tenantId, item) {
    const metadata = item.metadata || {};
    const channel = metadata.channel;
    const sender = this.senders.get(channel);
    const logBase = {
      tenant_id: tenantId,
      session_id: metadata.sessionId || null,
      message_id: item.id,
      module: 'queue_dispatcher',
      event: 'queue_dispatch',
      step: 'process_item'
    };

    if (!sender) {
      await WhatsAppQueue.updateStatus(tenantId, item.id, 'failed', 'Unsupported channel');
      logError('Queue item failed: unsupported channel', {
        ...logBase,
        error: 'Unsupported channel'
      });
      eventBus.emit(Events.MESSAGE_FAILED, { tenantId, itemId: item.id, reason: 'unsupported_channel' });
      return { success: false, error: 'Unsupported channel' };
    }

    const shadow = Boolean(metadata.shadow);

    if (shadow) {
      await WhatsAppQueue.updateStatus(tenantId, item.id, 'sent');
      logInfo('Queue item processed in shadow mode', {
        ...logBase,
        step: 'shadow_skip'
      });
      eventBus.emit(Events.MESSAGE_SENT, { tenantId, itemId: item.id, shadow: true });
      return { success: true, shadow: true };
    }

    try {
      await WhatsAppQueue.updateStatus(tenantId, item.id, 'processing');
      const startedAt = Date.now();
      const result = await sender.send({
        tenantId,
        item,
        metadata
      });
      const duration = Date.now() - startedAt;

      if (result && result.success) {
        await WhatsAppQueue.updateStatus(tenantId, item.id, 'sent');
        logInfo('Queue item sent', {
          ...logBase,
          step: 'sent',
          duration
        });
        eventBus.emit(Events.MESSAGE_SENT, {
          tenantId,
          itemId: item.id,
          messageId: result.messageId || null,
          channel
        });
        const delayMs = metadata.rateLimitMs ? Number(metadata.rateLimitMs) : 0;
        if (delayMs > 0) {
          await new Promise(resolve => setTimeout(resolve, delayMs));
        }
        return { success: true, messageId: result.messageId || null };
      }

      const errorMessage = result?.error || 'Unknown send error';
      const attempts = Number(item.attempts || 0);
      const maxAttempts = metadata.maxAttempts ? Number(metadata.maxAttempts) : 3;
      if (attempts + 1 < maxAttempts) {
        const backoffMs = Math.min(60000, 1000 * Math.pow(2, attempts + 1));
        await WhatsAppQueue.scheduleRetry(tenantId, item.id, backoffMs, errorMessage);
        logWarn('Queue item scheduled for retry', {
          ...logBase,
          step: 'retry_scheduled',
          error: errorMessage
        });
        return { success: false, error: errorMessage, retryScheduled: true };
      }

      await WhatsAppQueue.updateStatus(tenantId, item.id, 'failed', errorMessage);
      logError('Queue item failed after retries', {
        ...logBase,
        step: 'failed',
        error: errorMessage
      });
      eventBus.emit(Events.MESSAGE_FAILED, { tenantId, itemId: item.id, reason: errorMessage });
      return { success: false, error: errorMessage };
    } catch (error) {
      const errorMessage = error.message || String(error);
      await WhatsAppQueue.updateStatus(tenantId, item.id, 'failed', errorMessage);
      logError('Queue item processing error', {
        ...logBase,
        step: 'exception',
        error: errorMessage
      });
      eventBus.emit(Events.MESSAGE_FAILED, { tenantId, itemId: item.id, reason: errorMessage });
      return { success: false, error: errorMessage };
    }
  }
}

module.exports = new QueueDispatcher();
