/**
 * Etherlink custom relay: monitor source for messages to 42793, queue, call EtherlinkRelayReceiver.relayMintOrUnlock.
 * When CCIP does not support Etherlink. See docs/bridge/ETHERLINK_RELAY_RUNBOOK.md.
 */
import { ethers } from 'ethers';
import { config } from './config.js';
import { EtherlinkRelayReceiverABI } from './abis.js';
import * as metrics from './metrics.js';

export class EtherlinkRelayService {
  constructor(logger) {
    this.logger = logger;
    this.queue = [];
    this.inFlight = 0;
  }

  async start() {
    if (!config.etherlinkRelayBridge) {
      throw new Error('ETHERLINK_RELAY_BRIDGE required');
    }
    if (!config.relayPrivateKey) {
      throw new Error('ETHERLINK_RELAY_PRIVATE_KEY or PRIVATE_KEY required');
    }
    this.etherlinkProvider = new ethers.JsonRpcProvider(config.etherlinkRpcUrl);
    this.signer = new ethers.Wallet(config.relayPrivateKey, this.etherlinkProvider);
    this.receiver = new ethers.Contract(config.etherlinkRelayBridge, EtherlinkRelayReceiverABI, this.signer);
    this.logger.info('Etherlink relay started', { receiver: config.etherlinkRelayBridge });
    setInterval(() => this.processQueue(), config.pollIntervalMs);
  }

  async pushMessage(messageId, token, recipient, amount) {
    if (this.queue.length + this.inFlight >= config.queueDepthLimit) {
      this.logger.warn('Queue depth limit reached', { limit: config.queueDepthLimit });
      return;
    }
    this.queue.push({ messageId, token, recipient, amount });
    metrics.incrementDetected();
    metrics.setQueueDepth(this.queue.length + this.inFlight);
  }

  async processQueue() {
    while (this.queue.length > 0 && this.inFlight < config.maxConcurrent) {
      const msg = this.queue.shift();
      this.inFlight++;
      metrics.setQueueDepth(this.queue.length + this.inFlight);
      this.submit(msg).finally(() => {
        this.inFlight--;
        metrics.setQueueDepth(this.queue.length + this.inFlight);
      });
    }
  }

  async submit({ messageId, token, recipient, amount }) {
    try {
      await this.receiver.relayMintOrUnlock(messageId, token, recipient, amount);
      metrics.incrementSuccess();
    } catch (err) {
      this.logger.error('relayMintOrUnlock failed', { messageId, error: err.message });
      metrics.incrementFailed();
    }
  }
}
