import { EtherDenomination } from '../constants/common';
import { CHAIN_IDS } from '../constants/network';
import { TransactionEnvelopeType } from '../constants/transaction';
import { Numeric } from '../modules/Numeric';
import {
  calcGasTotal,
  calcTokenAmount,
  getSwapsTokensReceivedFromTxMeta,
  TOKEN_TRANSFER_LOG_TOPIC_HASH,
} from './transactions-controller-utils';

describe('transaction controller utils', () => {
  describe('calcGasTotal()', () => {
    it('should correctly compute gasTotal', () => {
      const result = calcGasTotal(12, 15);
      expect(result).toStrictEqual('17a');
    });
  });

  describe('getSwapsTokensReceivedFromTxMeta', () => {
    it('returns null if txMeta is not well formed', () => {
      expect(getSwapsTokensReceivedFromTxMeta('ETH', {}, '0x00')).toBe(null);
    });

    it('returns null if tokenSymbol is the default for network but txMeta does not contain a receipt', () => {
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          {},
          '0x00',
          '8',
          {},
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(null);
    });

    it('returns null if tokenSymbol is the default for network but txMeta does not contain a preTxBalance', () => {
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          { txReceipt: {}, postTxBalance: '0xe' },
          '0x00',
          '8',
          {},
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(null);
    });

    it('returns null if tokenSymbol is the default for network but txMeta does not contain a postTxBalance', () => {
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          { txReceipt: {}, preTxBalance: '0xe' },
          '0x00',
          '8',
          {},
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(null);
    });

    it('returns estimated receiving amount if post and pre tx balances are the same', () => {
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          {
            txReceipt: {},
            preTxBalance: '0xe',
            postTxBalance: '0xe',
            swapMetaData: { token_to_amount: '0x1' },
          },
          '0x00',
          '0x00',
          '8',
          {},
          CHAIN_IDS.MAINNET,
        ),
      ).toBe('0x1');
    });

    it('returns postTxBalance minus preTxBalance less the cost of gas of the transaction', () => {
      const preTxBalance = new Numeric('5000000', 10, EtherDenomination.WEI);
      const postTxBalance = new Numeric('5500000', 10, EtherDenomination.WEI);
      const gasUsed = new Numeric('28000', 10).toPrefixedHexString();
      const effectiveGasPrice = new Numeric('21', 10).toPrefixedHexString();
      const gasCost = calcGasTotal(gasUsed, effectiveGasPrice);
      const ethReceived = postTxBalance
        .minus(preTxBalance.minus(gasCost, 16))
        .toDenomination(EtherDenomination.ETH)
        .round(6);
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          {
            txReceipt: {
              gasUsed,
              effectiveGasPrice,
              type: TransactionEnvelopeType.feeMarket,
            },
            preTxBalance: preTxBalance.toPrefixedHexString(),
            postTxBalance: postTxBalance.toPrefixedHexString(),
            swapMetaData: { token_to_amount: '0x1' },
          },
          '0x00',
          '0x00',
          '8',
          {},
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(ethReceived.toString());
    });

    it('returns postTxBalance minus preTxBalance less the cost of gas of the transaction and the approval tx', () => {
      const preTxBalance = new Numeric('5000000', 10, EtherDenomination.WEI);
      const postTxBalance = new Numeric('5500000', 10, EtherDenomination.WEI);
      const gasUsed = new Numeric('28000', 10).toPrefixedHexString();
      const gasUsedApproval = new Numeric('75000', 10).toPrefixedHexString();
      const effectiveGasPrice = new Numeric('21', 10).toPrefixedHexString();
      const effectiveGasPriceApproval = new Numeric(
        '15',
        10,
      ).toPrefixedHexString();
      const gasCost = new Numeric(
        calcGasTotal(gasUsed, effectiveGasPrice),
        16,
      ).add(calcGasTotal(gasUsedApproval, effectiveGasPriceApproval), 16);
      const ethReceived = postTxBalance
        .minus(preTxBalance.minus(gasCost))
        .toDenomination(EtherDenomination.ETH)
        .round(6);
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          {
            txReceipt: {
              gasUsed,
              effectiveGasPrice,
              type: TransactionEnvelopeType.feeMarket,
            },
            preTxBalance: preTxBalance.toPrefixedHexString(),
            postTxBalance: postTxBalance.toPrefixedHexString(),
            swapMetaData: { token_to_amount: '0x1' },
          },
          '0x00',
          '0x00',
          '8',
          {
            txReceipt: {
              gasUsed: gasUsedApproval,
              effectiveGasPrice: effectiveGasPriceApproval,
              type: TransactionEnvelopeType.feeMarket,
            },
          },
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(ethReceived.toString());
    });

    it('returns postTxBalance minus preTxBalance less the cost of gas of the transaction and the approval tx for legacy txs', () => {
      const preTxBalance = new Numeric('5000000', 10, EtherDenomination.WEI);
      const postTxBalance = new Numeric('5500000', 10, EtherDenomination.WEI);
      const gasUsed = new Numeric('28000', 10).toPrefixedHexString();
      const gasUsedApproval = new Numeric('75000', 10).toPrefixedHexString();
      const gasPrice = new Numeric('21', 10).toPrefixedHexString();
      const gasPriceApproval = new Numeric('15', 10).toPrefixedHexString();
      const gasCost = new Numeric(calcGasTotal(gasUsed, gasPrice), 16).add(
        calcGasTotal(gasUsedApproval, gasPriceApproval),
        16,
      );
      const ethReceived = postTxBalance
        .minus(preTxBalance.minus(gasCost))
        .toDenomination(EtherDenomination.ETH)
        .round(6);
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'ETH',
          {
            txReceipt: {
              gasUsed,
              type: TransactionEnvelopeType.legacy,
            },
            txParams: {
              gasPrice,
            },
            preTxBalance: preTxBalance.toPrefixedHexString(),
            postTxBalance: postTxBalance.toPrefixedHexString(),
            swapMetaData: { token_to_amount: '0x1' },
          },
          '0x00',
          '0x00',
          '8',
          {
            txReceipt: {
              gasUsed: gasUsedApproval,
              type: TransactionEnvelopeType.feeMarket,
            },
            txParams: {
              gasPrice: gasPriceApproval,
            },
          },
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(ethReceived.toString());
    });

    it('returns value from tokenTransferLogs if not default token for network', () => {
      const logs = [
        {
          topics: [TOKEN_TRANSFER_LOG_TOPIC_HASH, '', '0x00'],
          address: '0x00',
          data: new Numeric('10000', 10).toPrefixedHexString(),
        },
      ];
      expect(
        getSwapsTokensReceivedFromTxMeta(
          'USDC',
          {
            txReceipt: { logs, status: '0x1' },
          },
          '0x00',
          '0x00',
          '8',
          {
            txReceipt: {},
          },
          CHAIN_IDS.MAINNET,
        ),
      ).toBe(calcTokenAmount(logs[0].data, 8).toString(10), 6);
    });
  });
});