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.BigInteger;
import java.math.RoundingMode;
import java.math.BigInteger;

/**
 * TRON 链扫描器（TRC20）
 * 通过 TRON HTTP API 查询交易状态和 TRC20 转账金额
 *
 * @author auto
 * @date 2026-05-13
 */
@Slf4j
@Component
public class TronChainScanner implements ChainScanner {

    /** TRC20 Transfer 事件签名（keccak256 of "Transfer(address,address,uint256)"），与 EVM 相同 */
    private static final String TRANSFER_EVENT = "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";

    /** 地址长度（TRON base58 地址转 hex 后为 42 字符，取后 40 位） */
    private static final int TRON_HEX_ADDR_LEN = 40;

    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    public boolean supports(String networkName) {
        return "TRC20".equalsIgnoreCase(networkName);
    }

    @Override
    public BigDecimal queryConfirmedAmount(String rpcUrl, String txHash,
                                            String toAddress, String contractAddress, int decimals) {
        try {
            // 1. 调用 /wallet/gettransactioninfobyid 获取交易回执
            JSONObject txInfo = callTronApi(rpcUrl, "/wallet/gettransactioninfobyid",
                    buildHashParam(txHash));
            if (txInfo == null || txInfo.isEmpty()) {
                log.debug("[TRON] hash 未确认或不存在: {}", txHash);
                return null;
            }

            // 2. 有合约地址 → TRC20 代币转账，解析 log 事件
            if (contractAddress != null && !contractAddress.isEmpty()) {
                return parseTrc20Amount(txInfo, toAddress, contractAddress, decimals);
            }

            // 3. 无合约地址 → 原生 TRX 转账，精度固定 6（1 TRX = 10^6 sun）
            return parseTrxAmount(rpcUrl, txHash, toAddress);

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

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

    /**
     * 解析 TRC20 Transfer 事件日志，返回到账金额
     *
     * @param txInfo          交易信息
     * @param toAddress       期望收款地址（base58 或 hex）
     * @param contractAddress 合约地址（base58 或 hex）
     * @param decimals        代币精度
     * @return 到账金额，未匹配返回 null
     */
    private BigDecimal parseTrc20Amount(JSONObject txInfo, String toAddress,
                                         String contractAddress, int decimals) {
        JSONArray logs = txInfo.getJSONArray("log");
        if (logs == null || logs.isEmpty()) {
            return null;
        }
        for (int i = 0; i < logs.size(); i++) {
            JSONObject logEntry = logs.getJSONObject(i);

            // 校验合约地址（TRON log 中 address 为 hex，去掉前缀 41）
            String logAddress = logEntry.getString("address");
            if (!matchTronAddress(contractAddress, logAddress)) {
                continue;
            }

            JSONArray topics = logEntry.getJSONArray("topics");
            if (topics == null || topics.size() < 3) {
                continue;
            }

            // 校验 Transfer 事件签名
            if (!TRANSFER_EVENT.equalsIgnoreCase(topics.getString(0))) {
                continue;
            }

            // topics[2] 是 to 地址（32字节 hex，取后 40 字符）
            String toHex = topics.getString(2);
            String toInLog = toHex.substring(toHex.length() - TRON_HEX_ADDR_LEN);
            if (!matchTronAddress(toAddress, toInLog)) {
                continue;
            }

            // data 是转账金额（hex），除以 10^decimals 转为实际金额
            String data = logEntry.getString("data");
            BigInteger rawAmount = new BigInteger(data, 16);
            return new BigDecimal(rawAmount)
                    .divide(BigDecimal.TEN.pow(decimals), decimals, RoundingMode.DOWN);
        }
        return null;
    }

    /**
     * 查询原生 TRX 转账金额
     *
     * @param rpcUrl    节点地址
     * @param txHash    交易 hash
     * @param toAddress 期望收款地址
     * @return 到账金额，未匹配返回 null
     */
    private BigDecimal parseTrxAmount(String rpcUrl, String txHash, String toAddress) {
        JSONObject tx = callTronApi(rpcUrl, "/wallet/gettransactionbyid",
                buildHashParam(txHash));
        if (tx == null || tx.isEmpty()) {
            return null;
        }

        // 取 raw_data.contract[0].parameter.value
        JSONObject rawData = tx.getJSONObject("raw_data");
        if (rawData == null) {
            return null;
        }
        JSONArray contracts = rawData.getJSONArray("contract");
        if (contracts == null || contracts.isEmpty()) {
            return null;
        }
        JSONObject contract = contracts.getJSONObject(0);
        JSONObject value = contract.getJSONObject("parameter").getJSONObject("value");
        if (value == null) {
            return null;
        }

        // 校验收款地址
        String toAddrHex = value.getString("to_address");
        if (!matchTronAddress(toAddress, toAddrHex)) {
            return null;
        }

        // amount 单位 sun（1 TRX = 1,000,000 sun），精度固定 6
        long amountSun = value.getLongValue("amount");
        return new BigDecimal(amountSun).divide(BigDecimal.TEN.pow(6), 6, RoundingMode.DOWN);
    }

    /**
     * 构建包含 txID 的请求参数
     *
     * @param txHash 交易 hash
     * @return 请求体 JSON
     */
    private JSONObject buildHashParam(String txHash) {
        JSONObject param = new JSONObject();
        param.put("value", txHash);
        return param;
    }

    /**
     * 发起 TRON HTTP API 调用
     *
     * @param baseUrl  节点基础地址，如 https://api.trongrid.io
     * @param path     接口路径，如 /wallet/gettransactionbyid
     * @param body     请求体
     * @return 响应 JSON，失败返回 null
     */
    private JSONObject callTronApi(String baseUrl, String path, JSONObject body) {
        try {
            String url = baseUrl.replaceAll("/$", "") + path;
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<String> entity = new HttpEntity<>(body.toJSONString(), headers);

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

    /** TRON Base58 字母表 */
    private static final String BASE58_ALPHABET =
            "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

    /**
     * 比较 TRON 地址是否匹配（兼容 base58 和 hex 格式）
     * 链上 log 返回的 address 为 hex（可能含 41 前缀），数据库存储的为 base58
     *
     * @param expected 期望地址（base58 或 40位 hex）
     * @param actual   实际地址（hex，可能含 41 前缀）
     * @return 是否匹配
     */
    private boolean matchTronAddress(String expected, String actual) {
        if (expected == null || actual == null) {
            return false;
        }
        // actual：去掉 41 前缀，统一为 40 位小写 hex
        String actualHex = actual.toLowerCase();
        if (actualHex.startsWith("41") && actualHex.length() == 42) {
            actualHex = actualHex.substring(2);
        }

        String expectedNorm = expected.toLowerCase();
        // expected 已经是 40 位 hex，直接比较
        if (expectedNorm.length() == 40) {
            return expectedNorm.equals(actualHex);
        }
        // expected 是 base58 格式，解码为 hex 后比较
        String expectedHex = tronBase58ToHex(expected);
        return expectedHex != null && expectedHex.equals(actualHex);
    }

    /**
     * 将 TRON base58Check 地址解码为 20 字节地址的 hex 字符串（不含 41 前缀）
     * TRON base58 地址结构：1字节网络前缀(41) + 20字节地址 + 4字节校验 = 25字节
     *
     * @param base58Address TRON base58 地址，如 TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
     * @return 40 位小写 hex 地址，解码失败返回 null
     */
    private String tronBase58ToHex(String base58Address) {
        try {
            // 1. Base58 解码为大整数
            BigInteger value = BigInteger.ZERO;
            for (char c : base58Address.toCharArray()) {
                int digit = BASE58_ALPHABET.indexOf(c);
                if (digit < 0) {
                    return null;
                }
                value = value.multiply(BigInteger.valueOf(58)).add(BigInteger.valueOf(digit));
            }
            // 2. 转为字节数组，确保 25 字节（不足高位补0）
            byte[] raw = value.toByteArray();
            byte[] payload = new byte[25];
            int srcPos = Math.max(0, raw.length - 25);
            int destPos = Math.max(0, 25 - raw.length);
            System.arraycopy(raw, srcPos, payload, destPos, Math.min(raw.length, 25));
            // 3. 取第 1-20 字节（跳过首字节 41 前缀，跳过末尾 4 字节校验）
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i <= 20; i++) {
                sb.append(String.format("%02x", payload[i]));
            }
            return sb.toString();
        } catch (Exception e) {
            log.warn("[TRON] base58 地址解码失败: {}", base58Address);
            return null;
        }
    }
}
