const { ethers } = require('hardhat');
const { getContractByNetworkName } = require('./contract-config');

const network = process.env.HARDHAT_NETWORK;

let dao;
let account;
let validationPools;
let reputation;

const fetchReputation = async () => {
  reputation = await dao.balanceOf(account);
  console.log(`reputation: ${reputation}`);
};

const fetchValidationPool = async (poolIndex) => {
  const pool = await dao.validationPools(poolIndex);
  validationPools[poolIndex] = pool;
  return pool;
};

const fetchValidationPools = async () => {
  const count = await dao.validationPoolCount();
  console.log(`validation pool count: ${count}`);
  const promises = [];
  validationPools = [];
  for (let i = 0; i < count; i += 1) {
    promises.push(fetchValidationPool(i));
  }
  await Promise.all(promises);
};

const initialize = async () => {
  const DAOAddress = getContractByNetworkName(network, 'DAO');
  dao = await ethers.getContractAt('DAO', DAOAddress);
  [account] = await ethers.getSigners();
  const address = await account.getAddress();
  console.log(`account: ${address}`);
  await fetchReputation();
  await fetchValidationPools();
};

const poolIsActive = (pool) => {
  if (new Date() >= new Date(Number(pool.endTime) * 1000)) return false;
  if (pool.resolved) return false;
  return true;
};

const poolIsValid = (pool) => {
  const Work1Address = getContractByNetworkName(network, 'Work1');
  return pool.sender === Work1Address;
};

const stake = async (pool, amount) => {
  console.log(`staking ${amount} in favor of pool ${pool.id.toString()}`);
  await dao.stake(pool.id, amount, true);
  await fetchReputation();
};

const stakeEach = async (pools, amountPerPool) => {
  const promises = [];
  pools.forEach((pool) => {
    promises.push(stake(pool, amountPerPool));
  });
  await Promise.all(promises);
};

async function main() {
  await initialize();

  validationPools.forEach((pool) => {
    let status;
    if (poolIsActive(pool)) status = 'Active';
    else if (!pool.resolved) status = 'Ready to Evaluate';
    else if (pool.outcome) status = 'Accepted';
    else status = 'Rejected';
    console.log(`pool ${pool.id.toString()}, status: ${status}`);
  });

  // Stake half of available reputation on any active, valid pools
  const activeValidPools = validationPools.filter(poolIsActive).filter(poolIsValid);
  if (activeValidPools.length && reputation > 0) {
    const amountPerPool = reputation / BigInt(2) / BigInt(activeValidPools.length);
    await stakeEach(activeValidPools, amountPerPool);
  }

  // Listen for new validation pools
  dao.on('ValidationPoolInitiated', async (poolIndex) => {
    console.log(`pool ${poolIndex} started`);
    const pool = await fetchValidationPool(poolIndex);
    await fetchReputation();

    if (poolIsValid(pool)) {
    // Stake half of available reputation on this validation pool
      const amount = reputation / BigInt(2);
      await stake(pool, amount, true);
    } else {
      console.log(`pool sender ${pool.sender} is not recognized`);
    }
  });

  dao.on('ValidationPoolResolved', async (poolIndex, votePasses) => {
    console.log(`pool ${poolIndex} resolved, status: ${votePasses ? 'accepted' : 'rejected'}`);
    fetchValidationPool(poolIndex);
    fetchReputation();
  });
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});