import './App.css';
import { read, utils } from "xlsx";
import { ethers, formatEther, parseEther  } from "ethers";
import { useCallback, useEffect, useRef, useState } from 'react';
import { tokenAbi } from "./tokenAbi";

const contractAddress = "0x1ebA64fDe3BF54545c86B9e3bB40c72f50f8D012";

function App() {
  const abi = [{"inputs": [{"internalType": "address","name": "token","type": "address"},{"internalType": "address[]","name": "addresses","type": "address[]"},{"internalType": "uint256[]","name": "amounts","type": "uint256[]"},{"internalType": "bool","name": "isToken","type": "bool"}],"name": "batchTransferFrom","outputs": [{"internalType": "bool","name": "","type": "bool"}],"stateMutability": "payable","type": "function"}]

  const [from, setFrom] = useState("");
  const [toArray, setToArray] = useState([]);
  const [amountArray, setAmountArray] = useState([]);
  const isToken = true;
  const [provider, setProvider] = useState();
  const [text, setText] = useState("파일 업로드")
  const tokenRef = useRef(null);

  const handleFile = (file) => {
    if (!file) return;
    setText(file.name);

    const reader = new FileReader();
    reader.onload = (evt) => {
      try {
        const data = new Uint8Array(evt.target.result);
        const workbook = read(data, { type: 'array' });
        const sheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[sheetName];
        const jsonData = utils.sheet_to_json(worksheet, {raw:true});

        const filteredData = jsonData.filter(row => {
          return (
            typeof row.address === "string" &&
            !row.address.toUpperCase().includes("EX") &&
            !String(row.amount).toUpperCase().includes("EX")
          );
        });

        const addresses = filteredData.map((row) => row.address);
        const amount = filteredData.map((row) => {
          // 소수점 2자리까지 반올림 처리
          const rounded = Math.round(Number(row.amount) * 100) / 100;
          return parseEther(rounded.toFixed(2));
        });
        setToArray(addresses);
        setAmountArray(amount);
      } catch (error) {
        alert(error)
      }
      
    };
    reader.readAsArrayBuffer(file);
  };

  const handleDrop = (e) => {
    e.preventDefault();
    const file = e.dataTransfer.files[0];
    handleFile(file);
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    handleFile(file);
  };

  // const handleCheckboxChange = (e) => {
  //   setIsToken(e.target.checked);
  // };

  const handleDragOver = (e) => {
    e.preventDefault();
  };

  useEffect(()=>{
    if (!window.ethereum) return alert("MetaMask not installed");
    setProvider(new ethers.BrowserProvider(window.ethereum))
  },[])

  
  const walletProvider = useCallback(async ()=>{
    try {
      await window.ethereum.request({
        method: 'wallet_switchEthereumChain',
        params: [{ chainId: '0x38' }] //0x38
      });
    } catch (err) {
      await window.ethereum.request({
        method: 'wallet_addEthereumChain',
        params: [{
          chainId: '0x38', //0x38
          chainName: 'BNB Smart Chain Mainnet',
          rpcUrls: ['https://binance.llamarpc.com'],
          nativeCurrency: { 
            name: 'BNB',
            symbol: 'BNB',
            decimals: 18
          },
          blockExplorerUrls: ['https://bscscan.com/']
        }]
      });
    }
    
    await provider.send("eth_requestAccounts", []);
    
    window.ethereum.on("accountsChanged", (accounts) => {
      if (accounts.length > 0) {
        setFrom(accounts[0]);
      } else {
        setFrom(null);
      }
    });
    const signer = provider.getSigner();    
    const senderAddress = await signer;
    setFrom(senderAddress.address)
  },[provider])
  
  useEffect(()=>{
    if(provider){
      walletProvider()
    }

    return () => {
      if (window.ethereum?.removeListener) {
        window.ethereum.removeListener("accountsChanged", () => {});
        window.ethereum.removeListener("chainChanged", () => {});
      }
    };

  },[provider,walletProvider])

  const sendToken = async () => {
    const tokenAddress = tokenRef.current?.value;

    let totalAmount = 0;
    amountArray.map((row)=> totalAmount += parseFloat(formatEther(row)));

    const signer = await provider.getSigner();
    const contract = new ethers.Contract(contractAddress, abi, signer);

    const batchSize = 200;
    const loopCount = Math.ceil(toArray.length / batchSize);

    let status;

    if(toArray.length !== 0 && amountArray.length !== 0 && toArray.length === amountArray.length)
    {
      try {
        const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, signer); 

        let balance = isToken ? await tokenContract.balanceOf(from) : await provider.getBalance(from);
        console.log(balance, parseEther(totalAmount.toString()))
        if(balance >= parseEther(totalAmount.toString())){
          if(isToken)
          { 
            //token approve
            const tx = await tokenContract.approve(
              contractAddress,
              parseEther(totalAmount.toString())
            )
            await provider.waitForTransaction(tx.hash);
          }
          } else {
          throw new Error("잔액부족")
        }
      } catch (error) {
        alert(error)
      }
    } else {
      alert("양식이 맞지 않습니다.")
    }
    
    for (let i = 0; i < loopCount; i++) 
    {
      const start = i * batchSize;
      const end = start + batchSize;

      const chunkAddresses = toArray.slice(start, end);
      const chunkAmounts = amountArray.slice(start, end);
      const chunkTotal = chunkAmounts.reduce((acc, amt) => acc + parseFloat(formatEther(amt)), 0);
      console.log(tokenAddress, chunkAddresses, chunkAmounts, isToken,chunkTotal)
     
      //multisender
      try {
        const tx = await contract.batchTransferFrom(
          tokenAddress,
          chunkAddresses,
          chunkAmounts,
          isToken,
          {value : isToken ? 0 : parseEther(chunkTotal.toString())}
        );

        status = await provider.waitForTransaction(tx.hash);  
      } catch (error) {
        alert(error)        
      }
      
    }

    if(status){
      setText("파일 업로드")
      setToArray([]);
      setAmountArray([]);        
    }
    
  };

  return (
    <div className="App">      
      <h1>Admin MultiSender</h1>
      <div
        style={{
          width : "600px",
          height : "100px",
          margin : "0 Auto",
          display : "flex",
          flexDirection: "column",
        }}
        >
          {/* <div>
            <span style={{fontSize: "18px"}}>토큰 전송 </span>
            <input type="checkbox" onChange={handleCheckboxChange} />
          </div> */}
          <div>
            <span style={{fontSize: "18px"}}>토큰 컨트랙트 : </span>
            <input type="text" ref={tokenRef} placeholder='' style={{fontSize: "18px", marginTop : "10px", width : "450px"}} defaultValue={"0x36b8dE7c6B06B3f170003452114f0B8E6BcFEE18"}/>
          </div>
          <div>
            <span style={{fontSize: "18px"}}>보내는 계좌 : </span>
            <input type='text' placeholder={from} style={{fontSize: "18px", marginTop : "10px", width: "450px", border: "none"}} disabled />
          </div>
      </div>
      
      <input type="file" id="fileInput" style={{ display: "none" }} onChange={handleFileChange} />
      <label htmlFor="fileInput">
        <div
          id="upload"
          onDrop={handleDrop}
          onDragOver={handleDragOver}
          style={{
            // display : toArray.length == 0 ? "block" : "none",
            border: '2px dashed gray',
            padding: '50px',
            textAlign: 'center',
            marginTop: '50px',
            marginBottom: '20px',
            margin: "0 Auto",
            width: "200px"
          }}
        >
          {text}
        </div>
      </label>

      <button type='button' onClick={sendToken} style={{marginTop : "20px", padding: "8px", width: "100px", fontSize: "20px"}}>전송</button>
    </div>
  );
}

export default App;
