package com.bizzan.bitrade.job.chain;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * EVM 兼容链扫描器（ERC20 / BEP20）
 * 通过 JSON-RPC eth_getTransactionReceipt 查询交易状态
 * ERC20 和 BEP20 使用相同的 JSON-RPC 协议，仅 rpcUrl 不同
 *
 * @author auto
 * @date 2026-05-13
 */
@Slf4j
@Component
public class EvmChainScanner implements ChainScanner {

    /** ERC20 Transfer 事件签名 keccak256 */
    private static final String TRANSFER_TOPIC =
            "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";

    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    public boolean supports(String networkName) {
        // 支持所有 EVM 兼容链
        return "ERC20".equalsIgnoreCase(networkName)
                || "BEP20".equalsIgnoreCase(networkName);
    }

    @Override
    public BigDecimal queryConfirmedAmount(String rpcUrl, String txHash,
                                            String toAddress, String contractAddress, int decimals) {
        try {
            // 1. 调用 eth_getTransactionReceipt
            JSONObject receipt = callRpc(rpcUrl, "eth_getTransactionReceipt",
                    new Object[]{txHash});
            if (receipt == null || receipt.getJSONObject("result") == null) {
                log.debug("[EVM] hash 未确认或不存在: {}", txHash);
                return null;
            }
            JSONObject result = receipt.getJSONObject("result");

            // 2. 校验交易状态：status=0x1 表示成功
            String status = result.getString("status");
            if (!"0x1".equals(status)) {
                log.warn("[EVM] 交易失败，hash={}, status={}", txHash, status);
                return null;
            }

            // 3. 有合约地址 → ERC20 代币转账，解析 Transfer 事件
            if (contractAddress != null && !contractAddress.isEmpty()) {
                return parseErc20Amount(result, toAddress, contractAddress, decimals);
            }

            // 4. 无合约地址 → 原生币（ETH/BNB），精度固定 18
            return parseNativeAmount(rpcUrl, txHash, toAddress);

        } catch (Exception e) {
            log.error("[EVM] 查询 hash 异常, hash={}, error={}", txHash, e.getMessage());
            return null;
        }
    }

    // ----------------------------- 私有方法 -----------------------------

    /**
     * 解析 ERC20 Transfer 事件日志，返回到账金额
     *
     * @param receipt         交易回执
     * @param toAddress       期望收款地址
     * @param contractAddress 合约地址
     * @param decimals        代币精度
     * @return 到账金额，未匹配返回 null
     */
    private BigDecimal parseErc20Amount(JSONObject receipt, String toAddress,
                                         String contractAddress, int decimals) {
        JSONArray logs = receipt.getJSONArray("logs");
        if (logs == null || logs.isEmpty()) {
            return null;
        }
        for (int i = 0; i < logs.size(); i++) {
            JSONObject logEntry = logs.getJSONObject(i);
            // 校验合约地址
            String logContract = logEntry.getString("address");
            if (!contractAddress.equalsIgnoreCase(logContract)) {
                continue;
            }
            JSONArray topics = logEntry.getJSONArray("topics");
            if (topics == null || topics.size() < 3) {
                continue;
            }
            // 校验 Transfer 事件签名
            if (!TRANSFER_TOPIC.equalsIgnoreCase(topics.getString(0))) {
                continue;
            }
            // topics[2] 是 to 地址（32字节，取后20字节）
            String toInLog = "0x" + topics.getString(2).substring(26);
            if (!toAddress.equalsIgnoreCase(toInLog)) {
                continue;
            }
            // data 是转账金额（hex），除以 10^decimals 转为实际金额
            String data = logEntry.getString("data");
            BigInteger rawAmount = new BigInteger(data.substring(2), 16);
            return new BigDecimal(rawAmount)
                    .divide(BigDecimal.TEN.pow(decimals), decimals, RoundingMode.DOWN);
        }
        return null;
    }

    /**
     * 查询原生币转账金额（ETH/BNB）
     *
     * @param rpcUrl    节点地址
     * @param txHash    交易 hash
     * @param toAddress 期望收款地址
     * @return 到账金额，未匹配返回 null
     */
    private BigDecimal parseNativeAmount(String rpcUrl, String txHash, String toAddress) {
        JSONObject txResp = callRpc(rpcUrl, "eth_getTransactionByHash",
                new Object[]{txHash});
        if (txResp == null || txResp.getJSONObject("result") == null) {
            return null;
        }
        JSONObject tx = txResp.getJSONObject("result");
        String to = tx.getString("to");
        if (!toAddress.equalsIgnoreCase(to)) {
            return null;
        }
        // value 单位 wei，除以 10^18
        String valueHex = tx.getString("value");
        BigInteger wei = new BigInteger(valueHex.substring(2), 16);
        return new BigDecimal(wei).divide(BigDecimal.TEN.pow(18), 18, RoundingMode.DOWN);
    }

    /**
     * 发起 JSON-RPC 调用
     *
     * @param rpcUrl 节点地址
     * @param method 方法名
     * @param params 参数
     * @return 响应 JSON，失败返回 null
     */
    private JSONObject callRpc(String rpcUrl, String method, Object[] params) {
        try {
            Map<String, Object> body = new HashMap<>();
            body.put("jsonrpc", "2.0");
            body.put("method", method);
            body.put("params", params);
            body.put("id", 1);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);

            ResponseEntity<String> response = restTemplate.postForEntity(
                    rpcUrl, entity, String.class);
            if (response.getStatusCode() == HttpStatus.OK) {
                return JSON.parseObject(response.getBody());
            }
        } catch (Exception e) {
            log.error("[EVM] RPC 调用失败, method={}, error={}", method, e.getMessage());
        }
        return null;
    }
}
