# src/light.js

    const ENS = require('eth-ens-namehash');
    const Constants = require('./const');
    const axios = require('axios');
    const bs58 = require('bs58');
    const sha256 = require('sha256');

    // Lovely ABIs
    const ResolverAbi = require('./smart_contracts/SV_ENS_Resolver.abi.json');
    const IndexAbi = require('./smart_contracts/SVLightIndex.abi.json');
    const BackendAbi = require('./smart_contracts/SVLightIndexBackend.abi.json');
    const BBFarmAbi = require('./smart_contracts/BBFarm.abi.json');
    const PaymentsAbi = require('./smart_contracts/SVPayments.abi.json');
    const AuxAbi = require('./smart_contracts/AuxAbi.abi.json');
    const AuctionAbi = require('./smart_contracts/CommAuctionIface.abi.json');
    const ERC20Abi = require('./smart_contracts/ERC20.abi.json');

    export const initializeSvLight = async svConfig => {
      const { indexContractName, ensResolver, httpProvider, auxContract } = svConfig;

      const Web3 = require('web3');
      const web3 = new Web3(new Web3.providers.HttpProvider(httpProvider));

      const resolver = new web3.eth.Contract(ResolverAbi, ensResolver);

      // const indexAddress =
      // console.log('indexAddress :', indexAddress);
      const index = new web3.eth.Contract(IndexAbi, await resolveEnsAddress({ resolver }, indexContractName));
      const backendAddress = await getBackendAddress({ index });
      const backend = new web3.eth.Contract(BackendAbi, backendAddress);
      const aux = new web3.eth.Contract(AuxAbi, auxContract);
      const payments = new web3.eth.Contract(PaymentsAbi, await index.methods.getPayments().call())

      return {
        svConfig,
        web3,
        resolver,
        index,
        backend,
        aux,
        payments
      };
    };

    export const resolveEnsAddress = async ({ resolver }, ensName) => {
      return await resolver.methods.addr(ENS.hash(ensName)).call();
    };

    export const getBackendAddress = async ({ index }) => {
      return await index.methods.getBackend().call();
    };

    export const getDemocInfo = async ({ backend, democHash }) => {
      return await backend.methods.getDInfo(democHash).call();
    };

    export const getDemocNthBallot = async ({ svNetwork }, democBallotInfo) => {
      // Destructure and set the variables that are needed
      const { index, backend, aux, svConfig } = svNetwork;
      const { democHash, nthBallot } = democBallotInfo;
      const indexAddress = index._address;
      const backendAddress = backend._address;
      const archiveUrl = { svConfig };

      const bbFarmAndBallotId = await aux.methods.getBBFarmAddressAndBallotId(backendAddress, indexAddress, democHash, nthBallot).call();
      // console.log('bbFarmAndBallotId :', bbFarmAndBallotId);

      const { id, bbFarmAddress } = bbFarmAndBallotId;
      const userEthAddress = '0x0000000000000000000000000000000000000000';
      const ethBallotDetails = await aux.methods.getBallotDetails(id, bbFarmAddress, userEthAddress).call();

      const ballotSpec = await getBallotSpec(archiveUrl, ethBallotDetails.specHash);
      // console.log('ballotSpec :', ballotSpec);
      // .then(x => console.log('Then called', x))
      // .catch(x => console.log('Caught error', x));

      const ballotObject = { ...bbFarmAndBallotId, ...ethBallotDetails, data: { ...ballotSpec.data } };

      return ballotObject;
    };

    export const getBallotSpec = async (archiveUrl, ballotSpecHash) => {
      return new Promise((res, rej) => {
        let done = false;
        const doRes = obj => {
          if (!done) {
            done = true;
            res(obj);
          }
        };
        getBallotObjectFromIpfs(ballotSpecHash).then(doRes);
        setTimeout(() => {
          if (!done) {
            getBallotObjectFromS3(archiveUrl, ballotSpecHash)
              .then(doRes)
              .catch(error => {
                rej(new Error(error));
              });
          }
        }, 3500);
      });
    };

    export const getBallotObjectFromS3 = async (archiveUrl, ballotSpecHash) => {
      return axios.get(archiveUrl + ballotSpecHash + '.json');
    };

    export const getBallotObjectFromIpfs = async ballotSpecHash => {
      const ipfsUrl = 'https://ipfs.infura.io/api/v0/block/get?arg=';
      const cidHex = '1220' + ballotSpecHash.substr(2);
      const bytes = Buffer.from(cidHex, 'hex');
      const cid = bs58.encode(bytes);
      return await axios.get(ipfsUrl + cid);
    };

    // Take the svNetwork object and a democHash, will return all of the ballots from the democracy in an array
    export const getDemocBallots = async ({ svNetwork, democHash }) => {
      const { backend } = svNetwork;
      const democInfo = await getDemocInfo({ backend, democHash });

      // Throw an error if the democ info is not correct
      const {erc20, owner} = democInfo
      if (owner === '0x0000000000000000000000000000000000000000') {
        throw new Error('Democracy Hash does not resolve to a democracy')
      }

      // TODO - Work out where / how to push an errored ballot
      // Loop through and get all the ballots
      const numBallots = democInfo.nBallots;
      const ballotsArray = [];
      for (let i = 0; i < numBallots; i++) {
        ballotsArray[i] = await getDemocNthBallot({ svNetwork }, { democHash: democHash, nthBallot: i });
      }

      return ballotsArray;
    };

    /** Takes in the svNetwork object and returns all relevant addresses */
    export const getContractAddresses = async ({svNetwork}) => {
      const {index, resolver, backend, aux, svConfig} = svNetwork
      const { delegationContractName, lookupAddress } = svConfig

      return {
        indexAddress: index._address,
        backendAddress: backend._address,
        auxAddress: aux._address,
        lookupAddress: lookupAddress,
        resolverAddress: resolver._address,
        communityAuctionAddress: await index.methods.getCommAuction().call(),
        delegationAddress: await resolveEnsAddress({ resolver }, delegationContractName),
        paymentsAddress: await index.methods.getPayments().call(),
      }
    }

    export const weiToCents = async ({payments}, wei) => {
      return await payments.methods.weiToCents(wei).call()
    }

    export const getCommunityBallotPrice = async ({payments}, democHash) => {
      return await payments.methods.getNextPrice(democHash).call()
    }

    export const checkIfAddressIsEditor = async ({ svNetwork }, { userAddress, democHash }) => {
      const {backend} = svNetwork
      return await backend.methods.isDEditor(democHash, userAddress).call();
    };

    // Checks the current ethereum gas price and returns a couple of values
    export const getCurrentGasPrice = async () => {
      const gasStationInfo = await axios.get('https://ethgasstation.info/json/ethgasAPI.json')
      const {data} = gasStationInfo

      return {
        safeLow: data.safeLow / 10,
        average: data.average / 10,
        fast: data.fast / 10,
        fastest: data.fastest / 10,
      }
    }
    // Checks the ballot hash against the ballot content
    export const checkBallotHashBSpec = (ballotSpec, assertSpecHash) => {
        let contentHash = '0x' + sha256(JSON.stringify(ballotSpec, null, 2));
        if (assertSpecHash === contentHash) {
          return true;
        } else {
          return false;
        }
    }

    // Checks the ballot hash against a ballot global ballot object
    // Does this by destructuring the specHash and data out of it
    export const checkBallotHashGBallot = (ballotObject) => {
      const {data, specHash} = ballotObject
      return checkBallotHashBSpec(data, specHash)
    }