'use strict';

const messageLedger = require('./message-ledger');

class ReplyDispatcher {
  constructor({ send, now = () => new Date().toISOString(), ledger = messageLedger } = {}) {
    if (typeof send !== 'function') throw new Error('ReplyDispatcher requires send function');
    this.send = send;
    this.now = now;
    this.ledger = ledger;
    this.buffer = [];
    this.seenDeliveryIds = new Set();
    this.statuses = [];
  }

  async dispatch(reply = {}) {
    const deliveryId = reply.deliveryId || `${reply.channel || 'channel'}:${reply.target || ''}:${hash(reply.message || '')}`;
    if (this.seenDeliveryIds.has(deliveryId)) {
      return this._status(deliveryId, 'skipped', 'duplicate');
    }
    this.seenDeliveryIds.add(deliveryId);
    if (reply.mode === 'buffered') {
      this.buffer.push({ ...reply, deliveryId });
      return this._status(deliveryId, 'buffered', '');
    }
    return this._sendNow({ ...reply, deliveryId });
  }

  async flush() {
    const pending = this.buffer.splice(0);
    const results = [];
    for (const reply of pending) {
      results.push(await this._sendNow(reply));
    }
    return results;
  }

  listStatuses() {
    return [...this.statuses];
  }

  async _sendNow(reply) {
    try {
      const result = await this.send(reply);
      this._recordLedgerReceipt(reply, result);
      return this._status(reply.deliveryId, 'sent', '', result);
    } catch (err) {
      return this._status(reply.deliveryId, 'failed', err.message);
    }
  }

  _recordLedgerReceipt(reply, result) {
    if (!reply?.ledger || !this.ledger?.recordOutboundDelivery) return;
    try {
      const messageId = resolveDeliveryMessageId(reply, result);
      if (!messageId) return;
      this.ledger.recordOutboundDelivery({
        channel: reply.ledger.channel || reply.channel,
        accountId: reply.ledger.accountId,
        conversationId: reply.ledger.conversationId || reply.target,
        threadId: reply.ledger.threadId || reply.threadTs,
        messageId,
        deliveryId: reply.deliveryId,
        source: reply.ledger.source || `${reply.channel || 'channel'}_outbound`,
        origin: reply.ledger.origin || 'assistant',
        content: reply.message,
        metadata: reply.ledger.metadata,
      });
    } catch (err) {
      console.warn(`[reply-dispatcher] Could not record channel delivery receipt: ${err.message}`);
    }
  }

  _status(deliveryId, status, reason = '', result = null) {
    const record = {
      deliveryId,
      status,
      reason,
      result,
      timestamp: this.now(),
    };
    this.statuses.push(record);
    return record;
  }
}

function resolveDeliveryMessageId(reply, result) {
  if (typeof reply?.ledger?.extractMessageId === 'function') {
    return reply.ledger.extractMessageId(result, reply);
  }
  return reply?.ledger?.messageId
    || reply?.messageId
    || result?.messageId
    || result?.message_id
    || result?.message_ts
    || result?.ts
    || result?.id
    || null;
}

function hash(text) {
  let h = 0;
  const input = String(text || '');
  for (let i = 0; i < input.length; i += 1) h = ((h << 5) - h + input.charCodeAt(i)) | 0;
  return Math.abs(h).toString(36);
}

module.exports = {
  ReplyDispatcher,
};
