import {useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {useWallet} from 'use-wallet';
import {useAddPopup, useBlockNumber} from '../application/hooks';
import {AppDispatch, AppState} from '../index';
import {checkedTransaction, finalizeTransaction} from './actions';
import {getDefaultProvider} from '../../utils/provider';

export function shouldCheck(
  lastBlockNumber: number,
  tx: {addedTime: number; receipt?: {}; lastCheckedBlockNumber?: number},
): boolean {
  if (tx.receipt) return false;
  if (!tx.lastCheckedBlockNumber) return true;
  const blocksSinceCheck = lastBlockNumber - tx.lastCheckedBlockNumber;
  if (blocksSinceCheck < 1) return false;
  const minutesPending = (new Date().getTime() - tx.addedTime) / 1000 / 60;
  if (minutesPending > 60) {
    // every 10 blocks if pending for longer than an hour
    return blocksSinceCheck > 9;
  } else if (minutesPending > 5) {
    // every 3 blocks if pending more than 5 minutes
    return blocksSinceCheck > 2;
  } else {
    // otherwise every block
    return true;
  }
}

export default function Updater(): null {
  const {chainId, ethereum} = useWallet();

  const lastBlockNumber = useBlockNumber();

  const dispatch = useDispatch<AppDispatch>();
  const state = useSelector<AppState, AppState['transactions']>((state) => state.transactions);

  const transactions = chainId ? state[chainId] ?? {} : {};

  // show popup on confirm
  const addPopup = useAddPopup();

  useEffect(() => {
    if (!chainId || !ethereum || !lastBlockNumber) {
      return;
    }

    const provider = getDefaultProvider();
    Object.keys(transactions)
      .filter((hash) => shouldCheck(lastBlockNumber, transactions[hash]))
      .forEach((hash) => {
        provider
          .getTransactionReceipt(hash)
          .then((receipt) => {
            if (receipt) {
              dispatch(
                finalizeTransaction({
                  chainId,
                  hash,
                  receipt: {
                    blockHash: receipt.blockHash,
                    blockNumber: receipt.blockNumber,
                    contractAddress: receipt.contractAddress,
                    from: receipt.from,
                    status: receipt.status,
                    to: receipt.to,
                    transactionHash: receipt.transactionHash,
                    transactionIndex: receipt.transactionIndex,
                  },
                }),
              );

              addPopup(
                {
                  txn: {
                    hash,
                    success: receipt.status === 1,
                    summary: transactions[hash]?.summary,
                  },
                },
                hash,
              );
            } else {
              dispatch(checkedTransaction({chainId, hash, blockNumber: lastBlockNumber}));
            }
          })
          .catch((error) => {
            console.error(`failed to check transaction hash: ${hash}`, error);
          });
      });
  }, [chainId, ethereum, transactions, lastBlockNumber, dispatch, addPopup]);

  return null;
}
