package com.table.transfer.module.service;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.table.transfer.module.enpty.ChainCoinDetail;
import com.table.transfer.module.enpty.TransactionInfoDTO;
import com.table.transfer.module.enpty.TransferRequest;
import com.table.transfer.module.enpty.TransferResponse;
import com.table.transfer.util.CryptoUtil;
import com.table.transfer.util.aUtil;
import com.table.transfer.util.ether.ChainCoinDetailManager;
import com.table.transfer.util.ether.EtherNonceManager;
import com.table.transfer.util.ether.Web3jManager;
import com.table.transfer.util.encryp.RSAEncryptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.rmi.ServerException;
import java.util.*;

@Slf4j
@Service
public class EthereumService {

    //EVM 链上的 ERC20 固定的事件签名keccak256 哈希
    String TRANSFER_EVENT_SIGNATURE = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";

    // 默认 Gas Limit（保险值）
    public static final BigInteger DEFAULT_GAS_LIMIT_NATIVE = BigInteger.valueOf(21_000);
    public static final BigInteger DEFAULT_GAS_LIMIT_TOKEN = BigInteger.valueOf(120_000);

    /**
     * 获取当前网络 gas price，并设置“最低安全值”
     */
    public BigInteger getSafeGasPrice(TransferRequest transferRequest) throws IOException {
        BigInteger networkGasPrice = Web3jManager.getWeb3j(transferRequest.getChain()).ethGasPrice().send().getGasPrice();
        ChainCoinDetail chainCoinDetail = ChainCoinDetailManager.getChainCoinDetail(transferRequest.getAssetCode());
        //设置的最低值
        BigInteger minGasPrice = chainCoinDetail.getMinGasPrice();
        //低于最低手续费,就取最低值
        if (networkGasPrice.compareTo(minGasPrice) <= 0) {
            networkGasPrice = minGasPrice;
        } else {
            //设定的最低值就直接加价20%防止拥堵被抛弃
            networkGasPrice = networkGasPrice.multiply(BigInteger.valueOf(12)).divide(BigInteger.TEN);
        }

        BigInteger maxGasPrice = chainCoinDetail.getMaxGasPrice();
        if (networkGasPrice.compareTo(maxGasPrice) > 0) {
            throw new ServerException(StrUtil.format("手续费超过设定最大值:{},当前估算Price:{}", maxGasPrice, networkGasPrice));
        }
        return networkGasPrice;
    }

    public void validateTransferRequest(TransferRequest transferRequest) throws Exception {
        String pk = RSAEncryptionUtil.decrypt(transferRequest.getEncryptionPrivateKey());
        Credentials credentials = Credentials.create(pk);
        String fromAddress = credentials.getAddress();
        if (!transferRequest.getFromAddress().equalsIgnoreCase(fromAddress)) {
            throw new Exception("私钥和from地址不一致");
        }
        transferRequest.setPrivateKey(pk);
        // 验证私钥
        if (!CryptoUtil.validatePrivateKey(transferRequest.getPrivateKey())) {
            throw new Exception("无效的私钥格式");
        }

        // 验证地址
        if (!CryptoUtil.validateEthAddress(transferRequest.getToAddress())) {
            throw new Exception("无效的接收地址");
        }
        if (ChainCoinDetailManager.getChainCoinDetail(transferRequest.getAssetCode()) == null) {
            throw new Exception("不支持" + transferRequest.getAssetCode());
        }
    }


    public TransferResponse transfer(TransferRequest transferRequest) throws Exception {
        validateTransferRequest(transferRequest);
        //链
        String chain = transferRequest.getChain();
        //交易对
        String assetCode = transferRequest.getAssetCode();
        //接受地址
        String toAddress = transferRequest.getToAddress();
        //金额
        String amount = transferRequest.getAmount();
        //使用对应链的web3j
        Web3j web3j = Web3jManager.getWeb3j(transferRequest.getChain());
        //私钥
        String privateKey = transferRequest.getPrivateKey();
        //是否代币转账
        boolean isTokenTransfer = transferRequest.isTokenTransfer();
        //发送地址
        String fromAddress = transferRequest.getFromAddress();
        //合约地址
        String contractAddress = transferRequest.getContractAddress();
        //代币配置
        ChainCoinDetail chainCoinDetail = ChainCoinDetailManager.getChainCoinDetail(assetCode);

        Credentials credentials = Credentials.create(privateKey);
        //hash
        String sentTxHash;
        //nonce
        BigInteger nonce = BigInteger.ZERO;
        try {
            //4. 转换金额为wei
            BigInteger decimalsAmount = toWei(transferRequest.getAmount(), chainCoinDetail.getDecimal());
            if (decimalsAmount.compareTo(BigInteger.ONE) < 0) {
                return TransferResponse.error("转账金额过低");
            }


            //5. 构建交易
            RawTransaction rawTransaction;
            //3.估算gas和gaslimit 估算个屁，limit和price估算完全不靠谱
//                        BigInteger gasLimit = estimateGasSafely(transferRequest);
            BigInteger gasLimit = isTokenTransfer ? DEFAULT_GAS_LIMIT_TOKEN : DEFAULT_GAS_LIMIT_NATIVE;
            BigInteger currentGasPrice = getSafeGasPrice(transferRequest);
            log.info("gasLimit: {}，currentGasPrice {}", gasLimit, currentGasPrice);
            //代币转账
            if (isTokenTransfer) {
                // 构造代币转账的 function 调用
                Function function = new Function(
                        "transfer",
                        Arrays.asList(new Address(toAddress), new Uint256(decimalsAmount)),
                        Arrays.asList(new TypeReference<Bool>() {
                        })
                );
                String data = FunctionEncoder.encode(function);

                nonce = EtherNonceManager.getNonce(chain, fromAddress);

                log.info("【代币转账开始】发送者={}, 接收者={}, 代币合约={}, 金额={}, nonce={}", fromAddress, toAddress, contractAddress, decimalsAmount, nonce);
                // 构造 RawTransaction：目标是代币合约地址
                rawTransaction = RawTransaction.createTransaction(
                        nonce,
                        currentGasPrice,
                        gasLimit,
                        transferRequest.getContractAddress(),
                        BigInteger.ZERO,
                        data
                );
            } else {
                //主币转账
                log.info("【转账开始】账户={}，目标={}, 金额={}, nonce={}", fromAddress, toAddress, amount, nonce);
                rawTransaction = RawTransaction.createEtherTransaction(
                        nonce,
                        currentGasPrice,
                        gasLimit,
                        toAddress,
                        decimalsAmount
                );
            }

            //6.签名交易
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, chainCoinDetail.getChainId(), credentials);
            String hexValue = Numeric.toHexString(signedMessage);
            //7.发送交易
            EthSendTransaction response = web3j.ethSendRawTransaction(hexValue).send();

            if (response.hasError()) {
                String errorMsg = response.getError().getMessage();
                // 仅对 underpriced 错误重试（同一 nonce）
                if (errorMsg.contains("underpriced") || errorMsg.contains("replacement transaction underpriced")) {
                    log.warn(" 交易因手续费太低被拒，nonce={}，gasPrice={}", nonce, currentGasPrice);
                    throw new Exception("交易因手续费太低被拒: " + errorMsg);
                } else {
                    String f = StrUtil.format("{}转账未知异常{}", chain, errorMsg);
                    log.error(f);
                    throw new Exception(f);
                }
            }

            // 8.成功发送，拿到哈希
            sentTxHash = response.getTransactionHash();


            if (sentTxHash == null) {
                // 没拿到哈希
                log.error("交易已发送,且未提示异常,但是hash为空");
                throw new Exception("交易已发送,且未提示异常,但是hash为空");
            }
            log.info("交易已发送，TxHash: {}", sentTxHash);
            return TransferResponse.success(sentTxHash);

        } catch (Exception e) {
            String msg = StrUtil.format("{}转账失败: {}", assetCode, e.getMessage());
            log.error(msg);
            return TransferResponse.error(msg);
        }
    }

    /**
     * 获取gasLimit
     */
    public BigInteger estimateGasSafely(TransferRequest request) {
        String chain = request.getChain();
        boolean isTokenTransfer = request.isTokenTransfer();
        String fromAddress = request.getFromAddress();
        String toAddress = request.getToAddress();
        String contractAddress = request.getContractAddress();
        BigInteger amount = request.getAmountWei();
        Web3j web3j = Web3jManager.getWeb3j(chain);
        Transaction transaction;

        try {
            if (isTokenTransfer) {
                // 代币转账：调用合约的 transfer(to, amount)
                Function function = new Function(
                        "transfer",
                        Arrays.asList(
                                new Address(toAddress),
                                new Uint256(amount != null ? amount : BigInteger.ONE)
                        ),
                        List.of()
                );
                String data = FunctionEncoder.encode(function);
                transaction = Transaction.createEthCallTransaction(fromAddress, contractAddress, data);
            } else {
                // 主币转账：data 为空
                transaction = Transaction.createEthCallTransaction(fromAddress, toAddress, "", BigInteger.ONE);
            }

            EthEstimateGas estimate;

            try {
                estimate = web3j.ethEstimateGas(transaction).send();
                BigInteger gasUsed = estimate.getAmountUsed();

                return gasUsed;
            } catch (Exception e) {
                //删掉重试机制,减少响应时间,失败直接返回默认值
                log.error("Gas 估算失败{}", e.getMessage());
            }

            // 兜底：返回默认值
            BigInteger defaultValue = (transaction.getData() == null || transaction.getData().isEmpty()) ?
                    DEFAULT_GAS_LIMIT_NATIVE : // 主币
                    DEFAULT_GAS_LIMIT_TOKEN;   // 代币

            log.error("使用保险默认 Gas Limit: {}", defaultValue);
            return defaultValue;

        } catch (Exception e) {
            log.error("Gas 估算准备阶段异常: {}", e.getMessage());
            // 返回基于链类型的默认值
            return isTokenTransfer ? DEFAULT_GAS_LIMIT_TOKEN : DEFAULT_GAS_LIMIT_NATIVE;
        }
    }

    private BigInteger toWei(String amount, Integer decimal) throws Exception {
        // 简化的转换，实际需要处理小数
        BigDecimal pow = BigDecimal.TEN.pow(decimal);
        return new BigDecimal(amount).multiply(pow).toBigInteger();

    }


    /**
     * 目标地址代币余额
     */
    public BigDecimal getTokenBalance(String assetCode, String contractAddress, String targetAddress) throws Exception {

        Function function = new Function("balanceOf",
                List.of(new Address(targetAddress)),
                List.of(new TypeReference<Uint256>() {
                })
        );

        String data = FunctionEncoder.encode(function);
        Transaction transaction = Transaction.createEthCallTransaction(null, contractAddress, data);

        EthCall response = Web3jManager.getWeb3j(aUtil.assetCode2Chain(assetCode)).ethCall(transaction, DefaultBlockParameterName.LATEST).send();
        log.info("EthCall response: {}", JSONUtil.toJsonStr(response));
        List<Type> result = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());

        if (result.isEmpty()) {
            throw new Exception("获取代币余额失败");
        }

        BigInteger balanceWei = (BigInteger) result.get(0).getValue();
        log.info("Raw balance: {}", balanceWei);

        int decimals = ChainCoinDetailManager.getChainCoinDetail(assetCode).getDecimal(); // 如果需要精确，应该调用合约的 decimals() 方法
        BigDecimal balance = new BigDecimal(balanceWei)
                .divide(new BigDecimal(BigInteger.TEN.pow(decimals)), decimals, RoundingMode.DOWN);

        return balance;
    }


    /**
     * 获取目标地址代主币余额
     *
     * @param targetAddress
     * @return
     * @throws Exception
     */
    public BigDecimal getBalance(String chain, String targetAddress) throws Exception {

        EthGetBalance balanceResponse = Web3jManager.getWeb3j(chain).ethGetBalance(targetAddress, DefaultBlockParameterName.LATEST).send();
        Convert.Unit unit;
        if (chain.equalsIgnoreCase("ETH") || chain.equalsIgnoreCase("BSC")) {
            unit = Convert.Unit.ETHER;
        } else {
            throw new Exception("暂不支持的链");
        }
        return Convert.fromWei(balanceResponse.getBalance().toString(), unit);
    }

    public Long getLastBlock(String chain) throws IOException {
        EthBlockNumber blockNumber = Web3jManager.getWeb3j(chain).ethBlockNumber().send();
        return blockNumber.getBlockNumber().longValue();
    }


    /**
     * 校验地址有效性
     *
     * @param address
     * @return
     */
    public boolean validateAddress(String address) {
        return CryptoUtil.validateEthAddress(address);
    }

    /**
     * 通过hash获取交易信息
     */
    public TransactionInfoDTO getTransactionInfoByHash(String assetCode, String transactionHash) throws Exception {
        String chain = aUtil.assetCode2Chain(assetCode);
        ChainCoinDetail chainCoinDetail = ChainCoinDetailManager.getChainCoinDetail(assetCode);
        // 查询交易回执
        EthGetTransactionReceipt receiptResponse = Web3jManager.getWeb3j(chain).ethGetTransactionReceipt(transactionHash).send();
        Optional<TransactionReceipt> receiptOpt = receiptResponse.getTransactionReceipt();
        if (!receiptOpt.isPresent()) {
            log.warn("交易尚未上链: {}", transactionHash);
            throw new ServerException("交易尚未上链: " + transactionHash);
        }

        TransactionReceipt receipt = receiptOpt.get();
        // 必须有区块号（已上链）
        if (receipt.getBlockNumber() == null) {
            log.warn("交易回执无区块号: {}", transactionHash);
            throw new ServerException("交易回执无区块号: " + transactionHash);
        }

        //判断交易是否执行成功
        boolean isSuccess = "0x1".equals(receipt.getStatus()) || "1".equals(receipt.getStatus());

        if (!isSuccess) {
            log.warn("交易执行失败 (reverted): {}, status={}", transactionHash, receipt.getStatus());
            throw new ServerException(StrUtil.format("交易执行失败 (reverted): {}, status={}", transactionHash, receipt.getStatus()));
        }
        TransactionReceipt transactionReceipt = receiptOpt.orElseThrow(RuntimeException::new);


        // 解析 logs 获取真正的 to 地址和 amount
        List<Log> logs = transactionReceipt.getLogs();
        if (!logs.isEmpty()) {

            for (Log log : logs) {
                //确保有3个主题并且符合keccak256
                if (log.getTopics().size() >= 3 && TRANSFER_EVENT_SIGNATURE.equals(log.getTopics().get(0))) {

                    // 主题解析：主题0是事件签名，主题1是from，主题2是to
                    List<String> topics = log.getTopics();
                    String transferFrom = Numeric.toBigInt(topics.get(1)).toString(16);
                    transferFrom = "0x" + padZeroes(transferFrom, 40); // 补齐40位（20字节）

                    String transferTo = Numeric.toBigInt(topics.get(2)).toString(16);
                    transferTo = "0x" + padZeroes(transferTo, 40); // 补齐40位


                    String data = log.getData();
                    BigInteger amountValue = Numeric.toBigInt(data);

                    //计算gas费
                    BigInteger gasUsed = Numeric.toBigInt(transactionReceipt.getGasUsed().toByteArray());
                    BigInteger effectiveGasPrice = Numeric.toBigInt(transactionReceipt.getEffectiveGasPrice());
                    BigInteger totalGasFeeWei = gasUsed.multiply(effectiveGasPrice);
                    // todo eth和bsc位数一样，后续增加其他EVM币种若是位数不一样这里需要改适配
                    BigDecimal totalGasFeeEth = Convert.fromWei(totalGasFeeWei.toString(), Convert.Unit.ETHER);

                    TransactionInfoDTO transactionInfoDTO = new TransactionInfoDTO();
                    transactionInfoDTO.setStatus("success");

                    Integer decimal = ChainCoinDetailManager.getChainCoinDetail(assetCode).getDecimal();
                    // USDT 是 6 位小数，所以除以 10^6

                    BigDecimal amount = new BigDecimal(amountValue).divide(new BigDecimal(BigInteger.TEN.pow(decimal)), decimal, RoundingMode.DOWN);
                    transactionInfoDTO.setAmount(amount);
                    transactionInfoDTO.setGasFee(totalGasFeeEth);
                    transactionInfoDTO.setHash(transactionReceipt.getTransactionHash());
                    transactionInfoDTO.setFromAddress(transferFrom);
                    transactionInfoDTO.setToAddress(transferTo);
                    return transactionInfoDTO;
                }
            }
        }
        throw new Exception("ethscan解析Hash失败");
    }

    private static String padZeroes(String hex, int length) {
        if (hex.length() >= length) return hex;
        return "0".repeat(length - hex.length()) + hex;
    }
}