
//const NFTcardsWith20Nfts = artifacts.require(".NftCardsWith20Nfts.sol");
const Jungle = artifacts.require('JungleToken.sol');
const NFTCards = artifacts.require("NFTCards.sol");
const ProxyJungleContract = artifacts.require('JungleProxy.sol');
const ProxyNftContract = artifacts.require('NFTProxy.sol');


const { expect } = require('chai');
const { time } = require("@openzeppelin/test-helpers");
const truffleAssert = require('truffle-assertions');
contract("NFTcards", accounts => {
  const [deployerAccount, buyer, userThird] = accounts;
  let proxynft
  let proxytoken
  let v0_jungle
  let v0_nft
  let v1_jungle
  let v1_nft
  let impl_v0_jungle// -> JungleTokenInstance
  let impl_v0_nft//    -> instance


  beforeEach(async function () {
    const from = deployerAccount;

    proxynft = await ProxyNftContract.new({ from: deployerAccount })
    proxytoken = await ProxyJungleContract.new({ from: deployerAccount })
    v0_jungle = await Jungle.new();
    v0_nft = await NFTCards.new();
    accounts = await web3.eth.getAccounts();

    await proxytoken.addVersion('0', v0_jungle.address, { from });
    await proxytoken.createProxy('0', from, { from });
    proxyjungleAddr = await proxytoken.proxyAddress();
    JungleTokenInstance = await Jungle.at(proxyjungleAddr, { from });

    //await proxytoken.upgradeTo('0',{ from });

    await proxynft.addVersion('0', v0_nft.address, { from });
    await proxynft.createProxy('0', from, 1629198757, { from });
    proxyNftAddr = await proxynft.proxyAddress();
    instance = await NFTCards.at(proxyNftAddr);

    await instance.setNCTAddress(JungleTokenInstance.address, { from });
    await JungleTokenInstance.setCardAddress(instance.address, { from });

  });

    it("Testcase 1 :should mint nfts.", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      let bal = await instance.balanceOf(buyer);
      expect(bal.toString()).to.equals("2");
    });


    it("Testcase 2 : Nft buyer should get 500 Jungle token amount per NFT ", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      let bal = await JungleTokenInstance.balanceOf(buyer);
      expect(bal.toString()).to.equals(web3.utils.toWei("1000", "ether"));

    });

    it("Testcase 3 : Should revert if msg.value sent is not enough.", async () => {
      await truffleAssert.reverts(instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.05", "ether") }));
    });

    //change provenance
    it("Testcase 4 : owner can change provenance", async () => {
      await instance.changeProvenace("0x2B9C48448CE78a92f993ACe5bEED165b725039fb", { from: deployerAccount });
      expect(await instance.PROVENANCE()).to.equals("0x2B9C48448CE78a92f993ACe5bEED165b725039fb");
    });
    it("Testcase 5 : Should revert if msg.sender is not owner while changing provenance", async () => {
      await truffleAssert.reverts(instance.changeProvenace("0x2B9C48448CE78a92f993ACe5bEED165b725039fb", { from: userThird }));

    });

    //change Name
    it("Testcase 6 : Owner of NFT can change name of NFT", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });

      let OldNameOfNft = await instance.tokenNameByIndex(0);
      await instance.changeName("0", "Name", { from: buyer });
      let NewNameOfNft = await instance.tokenNameByIndex(0);
      expect(NewNameOfNft).to.not.equals(OldNameOfNft);

    });
    it("Testcase 7 : Should revert if msg.sender is not owner of NFT, while changing name", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });

      await truffleAssert.reverts(instance.changeName("0", "name2", { from: userThird }));

    });

    //change Price
    it("Testcase 8 : owner can change price of NFT", async () => {
      await instance.changePrice(web3.utils.toWei("4.5", "ether"), { from: deployerAccount });
      let price = await instance.NFTPrice();
      expect(price.toString()).to.equals(web3.utils.toWei("4.5", "ether"));

    });
    it("Testcase 9 : Should revert if msg.sender is not owner while changing price", async () => {
      await truffleAssert.reverts(instance.changePrice(web3.utils.toWei("4.5", "ether"), { from: userThird }));

    });

    it("Testcase 10 : Should revert if anyone tries to buy more Nfts than 20", async () => {
      await truffleAssert.reverts(instance.mintNFT(21, { from: buyer, value: web3.utils.toWei("0.0001974", "ether") }));
    });

    it("Testcase 11 : Should revert if anyone tries to buy 0 Nfts with mintNft() function", async () => {
      await truffleAssert.reverts(instance.mintNFT(0, { from: buyer, value: web3.utils.toWei("0", "ether") }));
    });

    it("Testcase 12 : Reverts if length of name string entered for changeName() is bigger than 25", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await truffleAssert.reverts(instance.changeName("0", "a1234567891234567891234"));
    });

    //is name reserved
    it("Testcase 13 : Reverts if someone tries to set already reserved name to another NFT", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await instance.changeName("0", "abcd", { from: buyer });
      await truffleAssert.reverts(instance.changeName("1", "abcd", { from: buyer }));
    });

    it("Testcase 14 : Reverts if new name is same as current one.", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await instance.changeName("0", "abcd", { from: buyer });
      await truffleAssert.reverts(instance.changeName("0", "abcd", { from: buyer }));
    });

    //change MAX_NFT_SUPPLY to 20 before running this test
    // it("Testcase 20 : Reverts if sale has already ended.",async ()=>{
    //   await instance.mintNFT(20, { from: buyer, value: web3.utils.toWei("1.6","ether")});
    //   let a=await instance.MAX_NFT_SUPPLY();
    //   console.log(a.toString());
    //   await truffleAssert.reverts(instance.mintNFT(1, { from: buyer, value: web3.utils.toWei("0.08","ether")}));
    // });

    it("Testcase 15 : Anyone can remove own NFt name", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await instance.changeName("0", "abcd", { from: buyer });
      await instance.removeName(0, { from: buyer });
      expect(await instance.tokenNameByIndex(0)).to.equals("")
    });

    it("Testcase 16 : Anyone can't remove name of other's NFt", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await instance.changeName("0", "abcd", { from: buyer });
      await truffleAssert.reverts(instance.removeName(0, { from: userThird }));

    });

    //"Can't remove name of unnamed token
    it("Testcase 17 : Can't remove name of unnamed token", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });

      await truffleAssert.reverts(instance.removeName(0, { from: buyer }));

    });
    //Cannot set invalid name 
    it("Testcase 18 : Cannot set invalid name [Kevin1]", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await truffleAssert.reverts(instance.changeName("0", "Kevin1", { from: buyer }));
    });
    it("Testcase 19 : Cannot set invalid name [kevin_]", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await truffleAssert.reverts(instance.changeName("0", "kevin_", { from: buyer }));
    });
    it("Testcase 20 : Cannot set invalid name [keVin1]", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await truffleAssert.reverts(instance.changeName("0", "keVin1", { from: buyer }));
    });

    it("Testcase 21 : It reverts if user tries to breed card before 3 months", async () => {
      await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
      await truffleAssert.reverts(instance.breed("0", "1", { from: buyer }));
    });

  describe('-time dependent tests-.', () => {

    beforeEach(async () => {
      await instance.mintNFT(20, { from: buyer, value: web3.utils.toWei("1", "ether") });//Minting 20 times to get ERC20 tokens in account
      await instance.mintNFT(20, { from: buyer, value: web3.utils.toWei("1", "ether") });//which then will be used for breeding.
      balBefore = await JungleTokenInstance.balanceOf(buyer);
      balBefore = balBefore.toString();

      //Passing 90 days.
      threeMonths = await (time.duration.days(90));
      threeMonths = parseInt(threeMonths);

      // Increaseing time of Blockchain to by 90 days.
      await time.increase(threeMonths);
    });


    it("Testcase 22 : Can breed two cards.", async () => {
      await instance.breed("0", "1", { from: buyer });
      let totalSupply = await instance.totalSupply();
      expect(totalSupply.toString()).to.equals("41")
    });

    it("Testcase 23 : only owner can breed their cards.", async () => {
      await truffleAssert.reverts(instance.breed("0", "1", { from: userThird }));
    });

    it("Testcase 24 : Revert if user tries to breed unexisted NFTs.", async () => {
      await truffleAssert.reverts(instance.breed("0", "2", { from: userThird }));
    });

    it("Testcase 25 : It reverts if card isn't 21 days old while breeding", async () => {
      await instance.mintNFT(4, { from: buyer, value: web3.utils.toWei("0.2", "ether") });
      await truffleAssert.reverts(instance.breed("41", "42", { from: buyer }));
    });

    it("Testcase 26 : Generation one can breed max 8 times", async () => {
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });
      await instance.breed("5", "6", { from: buyer });


      await truffleAssert.reverts(instance.breed("5", "6", { from: buyer }));
    });

    // it("Testcase 27 : Generation two can breed max 5 times", async () => {
    //   //Generation 2 is starting from token index 20.
    //   await instance.breed("21", "22", { from: buyer });
    //   await instance.breed("21", "22", { from: buyer });
    //   await instance.breed("21", "22", { from: buyer });
    //   await instance.breed("21", "22", { from: buyer });
    //   await instance.breed("21", "22", { from: buyer });

    //   await truffleAssert.reverts(instance.breed("21", "22", { from: buyer }));
    // });

    it("Testcase 28 : getTokenBreedPrice() is working as expected.", async () => {
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });
      await instance.breed("0", "1", { from: buyer });

      balAfter = await JungleTokenInstance.balanceOf(buyer);

      balAfter = parseInt(balAfter);
      //console.log(balAfter);

      //150+300+450+600+750+900+1050+1200 = 5400
      balAfterSubtracting5400 = balBefore - (web3.utils.toWei("5400", "ether") * 2); // doing * 2 because of token getting burn for parent1 as well as parent 2.

      //console.log(await balAfterSubtracting5400.toString());
      expect(balAfter).to.equal(balAfterSubtracting5400);
    });

    it("Testcase 29 : User can breed two different generations.", async () => {
      let totalSupplyBefore = await instance.totalSupply();
      await instance.breed("5", "21", { from: buyer });
      let totalSupplyAfter = await instance.totalSupply();

      expect(Number(totalSupplyAfter)).to.equal(Number(totalSupplyBefore) + 1);
    });

    // it("Testcase 32: tokenURI", async () => {
    //   let tokenURI = await instance.tokenURI(1);
    //   console.log("--------------------------------------",tokenURI.toString());
    // });

  });

  // Wihtdraw function doesn't exists , in case of adding this functionality , test cases 30,31 can be uncommented!.

  it("Testcase 30 : Owner can withdraw all ethers.", async () => {

    let bal = await web3.eth.getBalance(deployerAccount);

    await instance.mintNFT(2, { from: buyer, value: web3.utils.toWei("0.1", "ether") });
    await instance.withdraw({ from: deployerAccount });

    let balAfter = await web3.eth.getBalance(deployerAccount);
    expect(balAfter).to.not.equals(bal + (web3.utils.toWei("0.1", "ether")));

  });

  it("Testcase 31 : mintReservedNfts()", async () => {
    await instance.mintReservedNfts([
      '0x633B398ad7909D53264806708817de7855A2D12E',
      '0x2B9C48448CE78a92f993ACe5bEED165b725039fb',
      '0x3753Cbac7Bebb9E02BF3f83d34F7B13C71a3E023',
      '0x777B5333F4D1Cd14DdBbaAd30C67B2a1291dAdF5'
    ], { from: deployerAccount });

    for (i = 0; i < 4; i++) {
      let bal = await instance.balanceOf(accounts[i]);
      expect(bal.toString()).to.equals('1');
    }
  });



  // it("Testcase 31 : Anyone without owner can't withdraw ethers from contract.", async () => {
  //   await truffleAssert.reverts(instance.withdraw({ from: userThird }));
  // });

});
