/**
 * Tezos relay: listen TezosBridgeInitiated -> (mock) Tezos mint/transfer -> confirmTransaction.
 */
import { ethers } from 'ethers';
import { config } from './config.js';
import { TezosAdapterABI } from './abis.js';
import * as metrics from './metrics.js';

export class TezosRelayService {
  constructor(logger) {
    this.logger = logger;
    this.provider = null;
    this.adapter = null;
    this.signer = null;
    this.queue = [];
    this.inFlight = 0;
  }

  async start() {
    if (!config.tezosAdapterAddress) {
      throw new Error('TEZOS_ADAPTER_ADDRESS required');
    }
    if (!config.oraclePrivateKey) {
      throw new Error('TEZOS_RELAY_ORACLE_KEY or PRIVATE_KEY required');
    }
    this.provider = new ethers.JsonRpcProvider(config.sourceChain.rpcUrl);
    this.signer = new ethers.Wallet(config.oraclePrivateKey, this.provider);
    this.adapter = new ethers.Contract(config.tezosAdapterAddress, TezosAdapterABI, this.signer);
    this.logger.info('Tezos relay started', { adapter: config.tezosAdapterAddress });
    this.adapter.on('TezosBridgeInitiated', (requestId, sender, token, amount, destination) => {
      this.queue.push({ requestId, sender, token, amount, destination });
      metrics.incrementEventsDetected();
      metrics.setPendingRequests(this.queue.length + this.inFlight);
      this.processQueue();
    });
  }

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

  async handleRequest({ requestId, sender, token, amount, destination }) {
    try {
      let tezosTxHash;
      if (config.mockTezosRelay) {
        tezosTxHash = `mock_${requestId.slice(0, 10)}_${Date.now()}`;
        this.logger.info('Mock Tezos tx', { requestId, destination, tezosTxHash });
      } else {
        tezosTxHash = await this.performTezosMintOrTransfer(requestId, sender, token, amount, destination);
      }
      await this.adapter.confirmTransaction(requestId, tezosTxHash);
      metrics.incrementConfirmationsSubmitted();
    } catch (err) {
      this.logger.error('confirmTransaction failed', { requestId, error: err.message });
      metrics.incrementConfirmationsFailed();
    }
  }

  /**
   * Perform actual Tezos mint/transfer via Taquito or Tezos RPC.
   * Real mint: set MOCK_TEZOS_RELAY=false, TEZOS_MINTER_ADDRESS, TEZOS_RPC_URL, TEZOS_ORACLE_SECRET_KEY (edsk...).
   * When @taquito/taquito is installed and config is set, calls minter contract mintOrTransfer(destination, amount); otherwise returns mock hash.
   */
  async performTezosMintOrTransfer(requestId, sender, token, amount, destination) {
    const { config } = await import('./config.js');
    if (!config.tezosRpcUrl) {
      return `mock_${requestId.slice(0, 10)}_${Date.now()}`;
    }
    try {
      const { Tezos } = await import('@taquito/taquito');
      const { InMemorySigner } = await import('@taquito/signer');
      const tezosSecret = process.env.TEZOS_ORACLE_SECRET_KEY || config.oraclePrivateKey;
      if (config.tezosMinterAddress && tezosSecret) {
        Tezos.setProvider({ rpc: config.tezosRpcUrl });
        Tezos.setSignerProvider(await InMemorySigner.fromSecretKey(tezosSecret));
        const contract = await Tezos.wallet.at(config.tezosMinterAddress);
        const op = await contract.methods.mintOrTransfer(destination, amount.toString()).send();
        await op.confirmation(1);
        return op.hash;
      }
    } catch (e) {
      this.logger.warn('Taquito mint/transfer failed, using mock hash', { error: e.message });
    }
    return `mock_${requestId.slice(0, 10)}_${Date.now()}`;
  }
}
