"""
MultiSender.py — Batch ERC-20 and native currency distribution tool.

Reads recipient lists from an Excel file (.xlsx) with columns:
  - Receiver: wallet address
  - Amount:   amount to send (in token units, not wei)

Supports any EVM-compatible chain (Ethereum, Arbitrum, Base, Polygon, Rayls, etc.)

Requirements:
  pip install web3 pandas openpyxl

Usage:
  python MultiSender.py
"""

from web3 import Web3
import pandas as pd
import time

# ERC-20 minimal ABI
CONTRACT_ABI = [
    {
        "constant": False,
        "inputs": [
            {"name": "to", "type": "address"},
            {"name": "value", "type": "uint256"}
        ],
        "name": "transfer",
        "outputs": [],
        "payable": False,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": True,
        "inputs": [{"name": "_owner", "type": "address"}],
        "name": "balanceOf",
        "outputs": [{"name": "balance", "type": "uint256"}],
        "payable": False,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": True,
        "inputs": [],
        "name": "decimals",
        "outputs": [{"name": "", "type": "uint8"}],
        "payable": False,
        "stateMutability": "view",
        "type": "function"
    }
]


def initialize_web3():
    """Prompt for RPC details and return a connected Web3 instance."""
    while True:
        try:
            rpc_url = input("Enter the RPC URL (e.g., https://rpc.minato.soneium.org/): ").strip()
            chain_id = int(input("Enter the Chain ID (e.g., 1946 for Soneium Minato Testnet): ").strip())
            explorer_url = input("Enter the Blockchain Explorer URL (optional, press Enter to skip): ").strip()

            web3_instance = Web3(Web3.HTTPProvider(rpc_url))
            if not web3_instance.is_connected():
                print("Failed to connect to the blockchain. Please check the RPC URL.")
                continue

            print("Connected to the blockchain successfully!")
            return web3_instance, chain_id, explorer_url
        except ValueError:
            print("Invalid input. Please enter valid values.")
        except Exception as e:
            print(f"An error occurred: {str(e)}. Please try again.")


def initialize_contract(web3):
    """Prompt for a token contract address and return the contract instance."""
    while True:
        try:
            contract_address = input("Enter the ERC-20 token contract address: ").strip()
            if not web3.is_checksum_address(contract_address):
                contract_address = web3.to_checksum_address(contract_address)

            token_contract = web3.eth.contract(address=contract_address, abi=CONTRACT_ABI)
            token_decimals = token_contract.functions.decimals().call()
            print(f"Contract initialized. Token Decimals: {token_decimals}")
            return token_contract
        except Exception as e:
            print(f"Invalid contract address. Error: {e}. Please try again.")


def send_native_currency(web3, recipient_address, amount, chain_id):
    """Send native currency (ETH, MATIC, etc.) to a single recipient."""
    try:
        recipient_address = web3.to_checksum_address(recipient_address)
        value_in_wei = web3.to_wei(amount, 'ether')
        eth_balance = web3.eth.get_balance(MY_ADDRESS)

        gas_price = web3.to_wei('0.51', 'gwei')
        gas_limit = 21000
        transaction_cost = gas_price * gas_limit

        if eth_balance < (transaction_cost + value_in_wei):
            print("Insufficient balance for the transaction!")
            return False

        txn = {
            'to': recipient_address,
            'value': value_in_wei,
            'gas': gas_limit,
            'gasPrice': gas_price,
            'nonce': web3.eth.get_transaction_count(MY_ADDRESS),
            'chainId': chain_id
        }
        signed_txn = web3.eth.account.sign_transaction(txn, PRIVATE_KEY)
        txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
        print(f"Transaction sent: {web3.to_hex(txn_hash)}")

        receipt = web3.eth.wait_for_transaction_receipt(txn_hash, timeout=120)
        if receipt.status == 1:
            print(f"Confirmed: {web3.to_hex(txn_hash)}")
            return True
        else:
            print(f"Failed: {web3.to_hex(txn_hash)}")
            return False

    except Exception as e:
        print(f"Error: {str(e)}")
        return False


def send_tokens(web3, token_contract, recipient_address, amount, chain_id):
    """Send ERC-20 tokens to a single recipient."""
    try:
        recipient_address = web3.to_checksum_address(recipient_address)
        value_in_wei = web3.to_wei(amount, 'ether')
        eth_balance = web3.eth.get_balance(MY_ADDRESS)

        gas_price = web3.to_wei('0.51', 'gwei')
        gas_limit = 40000
        transaction_cost = gas_price * gas_limit

        if eth_balance < transaction_cost:
            print("Insufficient ETH balance for gas fees!")
            return False

        txn = token_contract.functions.transfer(recipient_address, value_in_wei).build_transaction({
            'chainId': chain_id,
            'gas': gas_limit,
            'gasPrice': gas_price,
            'nonce': web3.eth.get_transaction_count(MY_ADDRESS),
        })
        signed_txn = web3.eth.account.sign_transaction(txn, PRIVATE_KEY)
        txn_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
        print(f"Transaction sent: {web3.to_hex(txn_hash)}")

        receipt = web3.eth.wait_for_transaction_receipt(txn_hash, timeout=120)
        if receipt.status == 1:
            print(f"Confirmed: {web3.to_hex(txn_hash)}")
            return True
        else:
            print(f"Failed: {web3.to_hex(txn_hash)}")
            return False

    except Exception as e:
        print(f"Error: {str(e)}")
        return False


def process_multi_transfer(web3, transfer_function, file_path, chain_id, token_contract=None):
    """
    Read an Excel file and execute transfers for each row.
    Excel must have 'Receiver' and 'Amount' columns.
    """
    try:
        data = pd.read_excel(file_path)

        if "Amount" not in data.columns or "Receiver" not in data.columns:
            print("Excel file must have 'Amount' and 'Receiver' columns.")
            return

        for index, row in data.iterrows():
            amount = row['Amount']
            recipient = row['Receiver']

            print(f"\nProcessing transaction {index + 1}: {amount} → {recipient}")

            if token_contract:
                success = transfer_function(web3, token_contract, recipient, amount, chain_id)
            else:
                success = transfer_function(web3, recipient, amount, chain_id)

            if not success:
                print("Transaction failed. Stopping further transactions.")
                break

    except FileNotFoundError:
        print("Excel file not found. Please check the file path.")
    except Exception as e:
        print(f"Error processing Excel file: {str(e)}")


if __name__ == "__main__":
    print("=== Velra MultiSender — Batch Token Distribution Tool ===\n")

    web3, chain_id, explorer_url = initialize_web3()
    PRIVATE_KEY = input("Enter your private key: ").strip()
    MY_ADDRESS = web3.eth.account.from_key(PRIVATE_KEY).address
    print(f"Sender address: {MY_ADDRESS}")

    while True:
        print("\nMenu:")
        print("1. Send Native Currency (ETH, MATIC, AVAX, etc.)")
        print("2. Send ERC-20 Tokens")
        print("3. Exit")

        choice = input("Enter your choice: ").strip()

        if choice == "1":
            print("\n1. Single Transfer")
            print("2. Multi-Transfer (Excel)")
            sub_choice = input("Enter your choice: ").strip()

            if sub_choice == "1":
                try:
                    amount = float(input("Enter the amount to send: "))
                    recipient_address = input("Enter the recipient's address: ")
                    send_native_currency(web3, recipient_address, amount, chain_id)
                except (ValueError, Exception) as e:
                    print(f"Error: {str(e)}")

            elif sub_choice == "2":
                file_path = input("Enter the path to the Excel file: ").strip()
                process_multi_transfer(web3, send_native_currency, file_path, chain_id)

        elif choice == "2":
            token_contract = initialize_contract(web3)
            print("\n1. Single Transfer")
            print("2. Multi-Transfer (Excel)")
            sub_choice = input("Enter your choice: ").strip()

            if sub_choice == "1":
                try:
                    amount = float(input("Enter the amount to send: "))
                    recipient_address = input("Enter the recipient's address: ")
                    send_tokens(web3, token_contract, recipient_address, amount, chain_id)
                except (ValueError, Exception) as e:
                    print(f"Error: {str(e)}")

            elif sub_choice == "2":
                file_path = input("Enter the path to the Excel file: ").strip()
                process_multi_transfer(web3, send_tokens, file_path, chain_id, token_contract)

        elif choice == "3":
            print("Exiting. Goodbye!")
            break

        else:
            print("Invalid choice. Please try again.")
