import { Contract, providers, utils } from "ethers";
import Head from "next/head";
import React, { useEffect, useRef, useState } from "react";
import Web3Modal from "web3modal";
import { abi, NFT_CONTRACT_ADDRESS, MAX_SUPPLY } from "../constants";
import styles from "../styles/Home.module.css";

export default function Home() {
  // walletConnected keep track of whether the user's wallet is connected or not
  const [walletConnected, setWalletConnected] = useState(false);
  // isPaused keeps track of whether the contract is paused or not
  const [isPaused, setPaused] = useState(true);
  // loading is set to true when we are waiting for a transaction to get mined
  const [loading, setLoading] = useState(false);
  // checks if the currently connected MetaMask wallet is the owner of the contract
  const [isOwner, setIsOwner] = useState(false);
  // checks if the address is in the whitelist
  const [isHolder, setIsHolder] = useState(false);
  // checks if the address is a giveaway winner
  const [isGiveaway, setIsGiveaway] = useState(false);
  // tokenIdsMinted keeps track of the number of tokenIds that have been minted
  const [tokenIdsMinted, setTokenIdsMinted] = useState("0");
  // Create a reference to the Web3 Modal (used for connecting to Metamask) which persists as long as the page is open
  const web3ModalRef = useRef();

  /**
   * holderMint: NFT Mint for Liquid Metal holders
   */
  const holderMint = async () => {
    try {
      // We need a Signer here since this is a 'write' transaction.
      const signer = await getProviderOrSigner(true);
      const nftContract = new Contract(
        NFT_CONTRACT_ADDRESS,
        abi,
        signer
      );
      // call the mint function from the contract, only whitelisted addresses would be able to mint
      const tx = await nftContract.mint();
      setLoading(true);
      // wait for the transaction to get confirmed
      await tx.wait();
      setLoading(false);
      window.alert("[LOG]: YOUR DRONE HAS BEEN MINTED");
    } catch (err) {
      console.error(err);
      alert(err.error.message);
    }
  };

  /**
   * giveawayMint: NFT mint for giveaway winners
   */
  const giveawayMint = async () => {
    try {
      // We need a Signer here since this is a 'write' transaction.
      const signer = await getProviderOrSigner(true);
      // Create a new instance of the Contract with a Signer, which allows
      // update methods
      const nftContract = new Contract(
        NFT_CONTRACT_ADDRESS,
        abi,
        signer
      );
      // call the giveaway mint function from the contract to mint the drone
      const tx = await nftContract.mintGiveaway();
      setLoading(true);
      // wait for the transaction to get mined
      await tx.wait();
      setLoading(false);
      window.alert("[LOG]: YOU HAVE CLAIMED YOUR DRONE.");
    } catch (err) {
      console.error(err);
      alert(err.error.message);
    }
  };

  /**
   * adminMint: allows the owner to reserve a set amount of tokens before the sale
   */
  const adminMint = async () => {
    try {
      const signer = await getProviderOrSigner(true);
      // update methods
      const nftContract = new Contract(
        NFT_CONTRACT_ADDRESS,
        abi,
        signer
      );
      let mintAmount = prompt("Enter the amount of tokens to reserve.", "0")
      const tx = await nftContract.mintAdmin(mintAmount);
      setLoading(true);
      // wait for the transaction to get mined
      await tx.wait();
      setLoading(false);
      window.alert("[LOG]: YOU HAVE MINTED YOUR DRONE.");
    } catch (err) {
      console.error(err);
    }
  };

  /*
      connectWallet: Connects the MetaMask wallet
    */
  const connectWallet = async () => {
    try {
      await getProviderOrSigner();
      setWalletConnected(true);
    } catch (err) {
      console.error(err);
    }
  };

  /**
   * unpauseContract: starts the sale for the NFT Collection
   */
  const unpauseContract = async () => {
    try {
      window.alert("This will disable the Admin Mint option.")
      const signer = await getProviderOrSigner(true);
      const nftContract = new Contract(
        NFT_CONTRACT_ADDRESS,
        abi,
        signer
      );
      const tx = await nftContract.pause(false);
      setLoading(true);
      await tx.wait();
      setLoading(false);
      window.alert("[LOG]: CONTRACT HAS BEEN UNPAUSED.")
      await checkIfPaused();
    } catch (err) {
      console.error(err);
    }
  };

  /**
   * checkIfPaused: checks if the sale has started by quering the `paused`
   * variable in the contract
   */
  const checkIfPaused = async () => {
    try {
      // Get the provider from web3Modal, which in our case is MetaMask
      // No need for the Signer here, as we are only reading state from the blockchain
      const provider = await getProviderOrSigner();
      // We connect to the Contract using a Provider, so we will only
      // have read-only access to the Contract
      const nftContract = new Contract(NFT_CONTRACT_ADDRESS, abi, provider);
      // call the presaleStarted from the contract
      const _isPaused = await nftContract.paused();
      console.log("isPaused: " + _isPaused);
      await getAddressInfo();
      setPaused(_isPaused);
      return isPaused;
    } catch (err) {
      console.error(err);
      return false;
    }
  };

  /**
   * getAddressInfo: calls the contract to retrieve the information about the address
   */
  const getAddressInfo = async () => {
    try {
      const provider = await getProviderOrSigner();
      const nftContract = new Contract(NFT_CONTRACT_ADDRESS, abi, provider);
      // call the owner function in the contract
      const _owner = await nftContract.owner();
      // Get the address of the connected MetaMask account
      const signer = await getProviderOrSigner(true);
      // Get the address associated to the signer which is connected to MetaMask
      const address = await signer.getAddress();
      // Check if address is in the liquid metal whitelist
      const _isHolder = await nftContract.isWhitelisted(address);
      // Check if address is in the giveaway whitelist
      //const _isGiveaway = await nftContract.isGiveaway(signer);
      if (address.toLowerCase() === _owner.toLowerCase()) {
        setIsOwner(true);
      }
      if (_isHolder) {
        setIsHolder(true);
      }
      if (_isGiveaway) {
        setIsGiveaway(true);
      }
    } catch (err) {
      console.error(err.message);
    }
  };

  /**
   * getTokenIdsMinted: gets the number of tokenIds that have been minted
   */
  const getTokenIdsMinted = async () => {
    try {
      // Get the provider from web3Modal, which in our case is MetaMask
      // No need for the Signer here, as we are only reading state from the blockchain
      const provider = await getProviderOrSigner();
      // We connect to the Contract using a Provider, so we will only
      // have read-only access to the Contract
      const nftContract = new Contract(NFT_CONTRACT_ADDRESS, abi, provider);
      // call the tokenIds from the contract
      const _tokenIds = await nftContract.totalSupply();
      //_tokenIds is a `Big Number`. We need to convert the Big Number to a string
      setTokenIdsMinted(_tokenIds.toString());
    } catch (err) {
      console.error(err);
    }
  };

  const getProviderOrSigner = async (needSigner = false) => {
    const provider = await web3ModalRef.current.connect();
    const web3Provider = new providers.Web3Provider(provider);

    const { chainId } = await web3Provider.getNetwork();
    if (chainId !== 4) {
      window.alert("Change the network to Rinkeby");
      throw new Error("Change network to Rinkeby");
    }

    if (needSigner) {
      const signer = web3Provider.getSigner();
      return signer;
    }
    return web3Provider;
  };

  useEffect(() => {
    // if wallet is not connected, create a new instance of Web3Modal and connect the MetaMask wallet
    if (!walletConnected) {
      web3ModalRef.current = new Web3Modal({
        network: "rinkeby",
        providerOptions: {},
        disableInjectedProvider: false,
      });
      connectWallet();

      // Check if sale has started
      checkIfPaused();

      // Update the amount of tokens minted
      getTokenIdsMinted();

      // Refresh the number of tokens minted every 5 seconds
      setInterval(async function () {
        await getTokenIdsMinted();
      }, 5 * 1000);
    }
  }, [walletConnected]);

  /*
      renderButton: Shows buttons depending on the traits of the address
    */
  const renderButton = () => {
    // If wallet is not connected, show a connection button
    if (!walletConnected) {
      return (
        <button onClick={connectWallet} className={styles.button}>
          CONNECT YOUR WALLET
        </button>
      );
    }

    // If an action is taking place, return the LOADING button
    if (loading) {
      return <button className={styles.button}>LOADING...</button>;
    }

    // If is Owner
    if (isOwner && isPaused) {
      return (
        <div>
          <button className={styles.button} onClick={adminMint}>MINT [ADMIN]</button>
          <button className={styles.button} onClick={unpauseContract}>[CONFIDENTIAL] : UNPAUSE CONTRACT</button>
        </div>
      );
    }

    // If contract is paused
    if (isPaused) {
      return (
        <div>
          <div className={styles.description}>[NOTICE] : MINT NOT LIVE</div>
        </div>
      );
    }

    // If contract is unpaused, allow minting while determining the available options for the user.
    if (!isPaused) {
      return (
        <div>
          <div className={styles.description}>
            MINTING STATUS: LIVE | CLAIM YOUR DRONE
          </div>
          {isHolder && <button className={styles.button} onClick={holderMint}>MINT [METAL]</button>}
          {isGiveaway && <button className={styles.button} onClick={giveawayMint}>MINT [GIVEAWAY]</button>}
          {(!isHolder && !isGiveaway && !isOwner) && <div>YOU ARE NOT WHITELISTED.</div>}
        </div>
      );
    }
  };

  return (
    <div>
      <Head>
        <title>DRONES by TRIPLE SIX</title>
        <meta name="description" content="DRONES by TRIPLE SIX Minting Site" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <div className={styles.main}>
        <div>
          <img className={styles.small_logo} src="./triplesix_small.jpg" />
          <h1 className={styles.title}>DRONES by TRIPLE SIX</h1>
          <div className={styles.description}>
            A exclusive free mint collection for holders of LIQUID METAL.
          </div>
          <div className={styles.description}>
            {tokenIdsMinted}/{MAX_SUPPLY} have been minted
          </div>
          {renderButton()}
        </div>
        <div className={styles.imageContainer}>
          <img className={styles.image} src="./drone_anim.gif" />
        </div>
      </div>

      <footer className={styles.footer}>
        <span>CREATED BY <a href="https://twitter.com/triplesixjewels" target="_blank"><u>TRIPLE SIX</u></a> | 2022</span>
      </footer>
    </div>
  );
}