package com.crm.ucard.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.core.constant.PayConstant;
import com.crm.rely.backend.core.dto.base.BaseResultDto;
import com.crm.rely.backend.core.dto.base.PageDto;
import com.crm.rely.backend.core.dto.base.ResultWithPagerDto;
import com.crm.rely.backend.core.dto.ucard.UcardWalletDepositTransactionDto;
import com.crm.rely.backend.core.dto.ucard.UcardWalletWithdrawDto;
import com.crm.rely.backend.core.dto.ucard.vaultody.VaultodyCreateCoinTransactionDto;
import com.crm.rely.backend.core.dto.ucard.vaultody.VaultodyDepositAddressDto;
import com.crm.rely.backend.core.entity.ucard.*;
import com.crm.rely.backend.core.entity.ucard.vaultody.VaultodyDepositAddressPageEntity;
import com.crm.rely.backend.core.entity.ucard.vaultody.VaultodyGenerateDepositAddressEntity;
import com.crm.rely.backend.core.entity.ucard.vaultody.VaultodyResetDepositAddressEntity;
import com.crm.rely.backend.core.entity.ucard.vaultody.callback.VaultodyCallbackEntity;
import com.crm.rely.backend.core.entity.ucard.vaultody.callback.VaultodyDataEntity;
import com.crm.rely.backend.core.entity.ucard.vaultody.callback.VaultodyItemEntity;
import com.crm.rely.backend.core.entity.ucard.vaultody.callback.VaultodyMinedInBlockEntity;
import com.crm.rely.backend.core.enums.CardApproveStatusEnum;
import com.crm.rely.backend.core.enums.CardOperateTypeEnum;
import com.crm.rely.backend.core.enums.CardWalletRecordTypeEnum;
import com.crm.rely.backend.core.enums.CardWalletWithdrawStatusEnum;
import com.crm.rely.backend.core.pojo.table.ucard.*;
import com.crm.rely.backend.exception.ServiceException;
import com.crm.rely.backend.service.EmailService;
import com.crm.rely.backend.util.DateUtil;
import com.crm.rely.backend.util.GetRandom;
import com.crm.rely.backend.util.HAMCSHA256Util;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.ucard.config.VaultodyConfig;
import com.crm.ucard.dao.mapper.CardCryptoCallbackRecordMapper;
import com.crm.ucard.dao.mapper.CardWalletAddressMapper;
import com.crm.ucard.dao.mapper.CardWalletWithdrawMapper;
import com.crm.ucard.dao.repository.*;
import com.crm.ucard.service.SysUcardConfigService;
import com.crm.ucard.service.UcardEmailService;
import com.crm.ucard.service.VaultodyService;
import com.crm.ucard.util.VaultodyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Connection;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
@Service
public class VaultodyServiceImpl implements VaultodyService {

    @Autowired
    private SysUcardConfigService sysUcardConfigService;
    @Autowired
    private CardWalletAddressRepository walletAddressRepository;
    @Autowired
    private CardWalletAddressMapper walletAddressMapper;
    @Autowired
    private CardWalletAddressRepository cardWalletAddressRepository;
    @Autowired
    private UcardVaultodyCallbackRecordRepository vaultodyCallbackRecordRepository;
    @Autowired
    private UcardWalletRepository ucardWalletRepository;
    @Autowired
    private UcardWalletRecordRepository ucardWalletRecordRepository;
    @Autowired
    private CardBlockchainConfigRepository blockchainConfigRepository;
    @Autowired
    private UcardEmailService ucardEmailService;
    @Autowired
    private CardCryptoCallbackRecordMapper cryptoCallbackRecordMapper;
    @Autowired
    private MessageSource messageSource;
    @Autowired
    private UcardWalletWithdrawRepository walletWithdrawRepository;
    @Autowired
    private CardWalletWithdrawMapper cardWalletWithdrawMapper;
    @Autowired
    private CardOperateRepository cardOperateRepository;
    @Autowired
    private EmailService emailService;
    @Autowired
    private CardBlockchainRateConfigRepository blockchainRateConfigRepository;

    public static VaultodyConfig getConfig() {
        VaultodyConfig config = new VaultodyConfig();
        config.setApiKey("f5dc4ad66d1dd85b15f46cdc570b8a13e9e8b825");
        config.setApiSecret("x+D75AW10JH2xg==");
        config.setPassphrase("Gaochao@7starpro.com");
        config.setMethod("POST");
        config.setNetwork("sepolia");
        config.setVaultId("691ad28a059b380007af005c");
        config.setBaseUrl("https://rest.vaultody.com");
        config.setAddressPathTemplate("/vaults/%s/%s/%s/addresses");
        return config;
    }

    public static String getSignature(String message, String apiSecret) {
        try {
            byte[] decodedSecret = Base64.getDecoder().decode(apiSecret);
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
            mac.init(secretKeySpec);
            byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));

            return Base64.getEncoder().encodeToString(hash);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Coin
     */
    public static void main4(String[] args) {
        try {
            // ------------------ 配置 ------------------
            String apiKey = "f5dc4ad66d1dd85b15f46cdc570b8a13e9e8b825";
            String apiSecret = "x+D75AW10JH2xg==";        // Base64编码的secret
            String passphrase = "Gaochao@7starpro.com";
            String method = "POST";
            String requestPath = "/vaults/691c1b59f19f990007704efe/ethereum/sepolia/transaction-requests";
            String baseUrl = "https://rest.vaultody.com";
            List<Map<String, Object>> recipientsList = new ArrayList<>();
            List<Map<String, Object>> sendersList = new ArrayList<>();
            Map<String, Object> recipientsMap = new HashMap<>();
            recipientsMap.put("address", "0x63f25f2ef69605ac3e60e549adc6854d3a4e8f61");
            recipientsMap.put("amount", "0.0001");
            recipientsList.add(recipientsMap);
            Map<String, Object> sendersMap = new HashMap<>();
            sendersMap.put("address", "0xa9160a2eb91ccce4ab44c532fc91a78ac21d806f");
            sendersList.add(sendersMap);

            Map<String, Object> labelMap = new HashMap<>();
            labelMap.put("feePriority", "slow");
//            labelMap.put("maxFee", "0.003");
            labelMap.put("note", "yourAdditionalInformationhere");
//            labelMap.put("prepareStrategy", "minimize-dust");
            labelMap.put("recipients", recipientsList);
            labelMap.put("senders", sendersList);
            Map<String, Object> itemMap = new HashMap<>();
            itemMap.put("item", labelMap);
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("data", itemMap);
            paramMap.put("context", "token-transaction-requests");

            // POST JSON body
            String body = "{\"data\":{\"item\":{\"label\":\"0a3b142570574b92a029bce7e2b0cb7b\"}}," +
                    "\"context\":\"yourExampleString\"}";
            String query = "{}"; // POST 时 query 通常为空，否则按接口要求填写

            // ------------------ 时间戳（秒） ------------------
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

            // ------------------ 构建消息用于签名 ------------------
            String message = timestamp + method + requestPath + JSON.toJSONString(paramMap) + query;

            String signature = getSignature(message, apiSecret);
            System.out.println("Signature: " + signature);
            Map<String, String> headers = new HashMap<>();
            headers.put("x-api-key", apiKey);
            headers.put("x-api-sign", signature);
            headers.put("x-api-timestamp", timestamp);
            headers.put("x-api-passphrase", passphrase);
            headers.put("Content-Type", "application/json");

            try {
                Connection.Response response = HttpUtil.post(baseUrl + requestPath, headers,
                        JSON.toJSONString(paramMap));
                System.out.println(response.statusCode());
                System.out.println(JSON.toJSONString(response.body()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * token
     */
    public static void main3(String[] args) {
        try {
            // ------------------ 配置 ------------------
            String apiKey = "f5dc4ad66d1dd85b15f46cdc570b8a13e9e8b825";
            String apiSecret = "x+D75AW10JH2xg==";        // Base64编码的secret
            String passphrase = "Gaochao@7starpro.com";
            String method = "POST";
            String requestPath = "/vaults/691c1b59f19f990007704efe/ethereum/sepolia/token-transaction-requests";
            String baseUrl = "https://rest.vaultody.com";

            Map<String, Object> labelMap = new HashMap<>();
            labelMap.put("amount", "0.0001");
            labelMap.put("contractAddress", "0xdac17f958d2ee523a2206206994597c13d831ec7");
            labelMap.put("feePriority", "slow");
            labelMap.put("fromAddress", "0xa9160a2eb91ccce4ab44c532fc91a78ac21d806f");
//            labelMap.put("maxFee", "0.004");
//            labelMap.put("note", "yourAdditionalInformationhere");
            labelMap.put("recipientAddress", "0x63f25f2ef69605ac3e60e549adc6854d3a4e8f61");
            Map<String, Object> itemMap = new HashMap<>();
            itemMap.put("item", labelMap);
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("data", itemMap);
            paramMap.put("context", "token-transaction-requests");

            // POST JSON body
            String body = "{\"data\":{\"item\":{\"label\":\"0a3b142570574b92a029bce7e2b0cb7b\"}}," +
                    "\"context\":\"yourExampleString\"}";
            String query = "{}"; // POST 时 query 通常为空，否则按接口要求填写

            // ------------------ 时间戳（秒） ------------------
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

            // ------------------ 构建消息用于签名 ------------------
            String message = timestamp + method + requestPath + JSON.toJSONString(paramMap) + query;

            String signature = getSignature(message, apiSecret);
            System.out.println("Signature: " + signature);
            Map<String, String> headers = new HashMap<>();
            headers.put("x-api-key", apiKey);
            headers.put("x-api-sign", signature);
            headers.put("x-api-timestamp", timestamp);
            headers.put("x-api-passphrase", passphrase);
            headers.put("Content-Type", "application/json");

            try {
                Connection.Response response = HttpUtil.post(baseUrl + requestPath, headers,
                        JSON.toJSONString(paramMap));
                System.out.println(response.statusCode());
                System.out.println(JSON.toJSONString(response.body()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * address
     */
    public static void main1(String[] args) {
        try {
            // ------------------ 配置 ------------------
            String apiKey = "9438107962b78257836bba29ca0eb254906a6e9b";
            String apiSecret = "pG9BU4s6N9ZB8g==";        // Base64编码的secret
            String passphrase = "Gaochao@7starpro.com";
            String method = "POST";
            String requestPath = "/vaults/693689584a27d60007c6e2ee/ethereum/sepolia/addresses";
            String baseUrl = "https://rest.vaultody.com";

            Map<String, Object> labelMap = new HashMap<>();
            labelMap.put("label", "0a31fd4cc0c8467c96918cf81551d7a6");
            Map<String, Object> itemMap = new HashMap<>();
            itemMap.put("item", labelMap);
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("data", itemMap);
            paramMap.put("context", "address");

            // POST JSON body
            String body = "{\"data\":{\"item\":{\"label\":\"0a31fd4cc0c8467c96918cf81551d7a6\"}}," +
                    "\"context\":\"yourExampleString\"}";
            String query = "{}"; // POST 时 query 通常为空，否则按接口要求填写

            // ------------------ 时间戳（秒） ------------------
            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

            // ------------------ 构建消息用于签名 ------------------
            String message = timestamp + method + requestPath + body + query;

            // ------------------ HMAC-SHA256 签名 ------------------
//            byte[] decodedSecret = Base64.getDecoder().decode(apiSecret);
//            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
//            SecretKeySpec secretKeySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
//            sha256_HMAC.init(secretKeySpec);
//            byte[] hash = sha256_HMAC.doFinal(message.getBytes(StandardCharsets.UTF_8));
//
//            String signatureB64 = Base64.getEncoder().encodeToString(hash);
            String signature = getSignature(message, apiSecret);
//            System.out.println("signatureB64: " + signatureB64);
            System.out.println("Signature: " + signature);
            Map<String, String> headers = new HashMap<>();
            headers.put("x-api-key", apiKey);
            headers.put("x-api-sign", signature);
            headers.put("x-api-timestamp", timestamp);
            headers.put("x-api-passphrase", passphrase);
            headers.put("Content-Type", "application/json");

            try {
                Connection.Response response = HttpUtil.post(baseUrl + requestPath, headers, body);
                System.out.println(response.statusCode());
                System.out.println(JSON.toJSONString(response.body()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
//        String baseUrl = "https://rest.vaultody.com";
//        String linkAddress = "/vaults/693a869e48199100070f850d/ethereum/sepolia/link-address";
//
//        // ------------------ 配置 ------------------
//        String apiKey = "9438107962b78257836bba29ca0eb254906a6e9b";
//        String apiSecret = "pG9BU4s6N9ZB8g==";        // Base64编码的secret
//        String passphrase = "Gaochao@7starpro.com";
//        String method = "POST";
////        String requestPath = "/vaults/693689584a27d60007c6e2ee/ethereum/sepolia/addresses";
//
//
//        Map<String, Object> labelMap = new HashMap<>();
//        labelMap.put("label", "0a31fd4cc0c8467c96918cf81551d7a6");
//        Map<String, Object> itemMap = new HashMap<>();
//        itemMap.put("item", labelMap);
//        Map<String, Object> paramMap = new HashMap<>();
//        paramMap.put("data", itemMap);
//        paramMap.put("context", "address");
//
//        // POST JSON body
//        String body = "{\"data\":{\"item\":{\"address\":\"0x06b4a10b4eb1372bd2be0c710c0c9d38daa6f1b2\"}}," +
//                "\"context\":\"yourExampleString1\"}";
//        String query = "{}"; // POST 时 query 通常为空，否则按接口要求填写
//
//        // ------------------ 时间戳（秒） ------------------
//        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
//
//        // ------------------ 构建消息用于签名 ------------------
//        String message = timestamp + method + linkAddress + body + query;
//
//        // ------------------ HMAC-SHA256 签名 ------------------
////            byte[] decodedSecret = Base64.getDecoder().decode(apiSecret);
////            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
////            SecretKeySpec secretKeySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
////            sha256_HMAC.init(secretKeySpec);
////            byte[] hash = sha256_HMAC.doFinal(message.getBytes(StandardCharsets.UTF_8));
////
////            String signatureB64 = Base64.getEncoder().encodeToString(hash);
//        String signature = getSignature(message, apiSecret);
////            System.out.println("signatureB64: " + signatureB64);
//        System.out.println("Signature: " + signature);
//        Map<String, String> headers = new HashMap<>();
//        headers.put("x-api-key", apiKey);
//        headers.put("x-api-sign", signature);
//        headers.put("x-api-timestamp", timestamp);
//        headers.put("x-api-passphrase", passphrase);
//        headers.put("Content-Type", "application/json");
//
//        try {
//            Connection.Response response = HttpUtil.post(baseUrl + linkAddress, headers, body);
//            System.out.println(response.statusCode());
//            System.out.println(response.statusMessage());
//            System.out.println(JSON.toJSONString(response.body()));
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }


    }

    private String getMessage(String code) throws ServiceException {
        return messageSource.getMessage(code, null, LocaleContextHolder.getLocale());
    }

    public String getPath(String pathTemplate, String vaultId, String blockchain, String network) {
//        String pathTemplate = "/vaults/%s/%s/%s/addresses";
        String actualPath = String.format(pathTemplate, vaultId, blockchain, network);
        return actualPath;
    }

    public VaultodyConfig getVaultodyConfig() throws Exception {
        SysUcardConfigTable sysUcardConfigTable = sysUcardConfigService.getByCode(Constants.VAULTODY_CONFIG);
        if (sysUcardConfigTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }
        VaultodyConfig vaultodyConfig = JSON.parseObject(sysUcardConfigTable.getValue(), VaultodyConfig.class);
        return vaultodyConfig;
    }

    /**
     * 生成钱包地址
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateDepositAddress(VaultodyGenerateDepositAddressEntity entity) throws Exception {
        // 查询数据库是否有未重置的地址
        CardWalletAddressTable table =
                walletAddressRepository.findFirstByUniqueIdAndBlockchainAndIsReset(entity.getUniqueId(),
                entity.getBlockchain(), false);
        if (table != null && StringUtils.isNotEmpty(table.getAddress())) {
            return table.getAddress();
        }
        VaultodyConfig config = getVaultodyConfig();
        String requestPath = getPath(config.getAddressPathTemplate(), config.getVaultId(), entity.getBlockchain(),
                config.getNetwork());
        String apiKey = config.getApiKey();
        String apiSecret = config.getApiSecret();
        String passphrase = config.getPassphrase();
        String method = config.getMethod();
        String baseUrl = config.getBaseUrl();

        Map<String, Object> labelMap = new HashMap<>();
        labelMap.put("label", entity.getUniqueId());
        Map<String, Object> itemMap = new HashMap<>();
        itemMap.put("item", labelMap);
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("data", itemMap);

        // POST JSON body
        String body = JSON.toJSONString(paramMap);
        String query = "{}"; // POST 时 query 通常为空，否则按接口要求填写

        // ------------------ 时间戳（秒） ------------------
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

        // ------------------ 构建消息用于签名 ------------------
        String message = timestamp + method + requestPath + body + query;

        String signature = getSignature(message, apiSecret);
        System.out.println("Signature: " + signature);
        Map<String, String> headers = new HashMap<>();
        headers.put("x-api-key", apiKey);
        headers.put("x-api-sign", signature);
        headers.put("x-api-timestamp", timestamp);
        headers.put("x-api-passphrase", passphrase);
        headers.put("Content-Type", "application/json");

        Connection.Response response = HttpUtil.post(baseUrl + requestPath, headers, body);
        if (response.statusCode() != 200) {
            log.error("用户: {} 钱包地址生成失败: {}", entity.getUniqueId(), response.body());
            throw new ServiceException("钱包地址生成失败");
        }
        // 解析 JSON 字符串
        JSONObject jsonObject = JSON.parseObject(response.body());

        // 获取 address 和 label 字段
        String address = jsonObject.getJSONObject("data").getJSONObject("item").getString("address");
        // 保存数据库数据
        table = new CardWalletAddressTable();
        table.setAddress(address);
        table.setBlockchain(entity.getBlockchain());
        table.setUniqueId(entity.getUniqueId());
        table.setIsReset(false);
        table.setAddTime(new Date());
        walletAddressRepository.save(table);
        return address;
    }

    /**
     * 链接钱包地址
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void linkAddress(String blockchain, String address) throws Exception {
        CardWalletAddressTable table = walletAddressRepository.findFirstByAddressAndBlockchain(address, blockchain);
        if (table == null) {
            log.error("链接钱包地址，钱包地址: {} 不存在", address);
            throw new ServiceException("钱包地址不存在");
        }
        if (StringUtils.isNotEmpty(table.getLinkAddressRequestId())) {
            return;
        }
        VaultodyConfig config = getVaultodyConfig();
        String requestPath = getPath(config.getLinkAddressPathTemplate(), config.getVaultId(), blockchain,
                config.getNetwork());
        String apiKey = config.getApiKey();
        String apiSecret = config.getApiSecret();
        String passphrase = config.getPassphrase();
        String method = config.getMethod();
        String baseUrl = config.getBaseUrl();

        Map<String, Object> labelMap = new HashMap<>();
        labelMap.put("address", address);
        Map<String, Object> itemMap = new HashMap<>();
        itemMap.put("item", labelMap);
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("data", itemMap);

        // POST JSON body
        String body = JSON.toJSONString(paramMap);
        String query = "{}"; // POST 时 query 通常为空，否则按接口要求填写

        // ------------------ 时间戳（秒） ------------------
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);

        // ------------------ 构建消息用于签名 ------------------
        String message = timestamp + method + requestPath + body + query;

        String signature = getSignature(message, apiSecret);
        System.out.println("Signature: " + signature);
        Map<String, String> headers = new HashMap<>();
        headers.put("x-api-key", apiKey);
        headers.put("x-api-sign", signature);
        headers.put("x-api-timestamp", timestamp);
        headers.put("x-api-passphrase", passphrase);
        headers.put("Content-Type", "application/json");

        Connection.Response response = HttpUtil.post(baseUrl + requestPath, headers, body);
        if (response.statusCode() != 200) {
            log.error("钱包地址: {} 链接失败: {}", address, response.body());
            throw new ServiceException("钱包地址链接失败");
        }
        // 解析 JSON 字符串
        JSONObject jsonObject = JSON.parseObject(response.body());

        // 获取 address 和 label 字段
        String transactionRequestId = jsonObject.getJSONObject("data").getJSONObject("item").getString(
                "transactionRequestId");
        // 保存数据库数据

        table.setLinkAddressRequestId(transactionRequestId);
        walletAddressRepository.save(table);
    }

    /**
     * ucard钱包提款申请
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto walletWithdrawApply(WalletWithdrawApplyEntity entity) throws Exception {
        ucardEmailService.existByEmail(entity.getEmail());
        emailService.validateCode(entity.getEmail(), entity.getEmailCode());
        UcardWalletTable walletTable = ucardWalletRepository.getFirstByUniqueId(entity.getUniqueId());
        if (walletTable == null) {
            throw new ServiceException("ucard.error.wallet.not.exist", getMessage("ucard.error.wallet.not.exist"));
        }
        if (walletTable.getBalance().compareTo(entity.getAmount()) < 0) {
            throw new ServiceException("ucard.error.wallet.balance.insufficient", getMessage("ucard.error.wallet" +
                    ".balance.insufficient"));
        }
        walletTable.setBalance(walletTable.getBalance().subtract(entity.getAmount()));
        ucardWalletRepository.save(walletTable);

        UcardWalletRecordTable recordTable = new UcardWalletRecordTable();
        recordTable.setWalletId(walletTable.getId());
        recordTable.setCId(walletTable.getCId());
        recordTable.setUniqueId(walletTable.getUniqueId());
        recordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        recordTable.setAmount(entity.getAmount().negate());
        recordTable.setFee(BigDecimal.ZERO);
        recordTable.setType(CardWalletRecordTypeEnum.CRYPTO_CURRENCY_TRANSFER_OUT.getCode());
        recordTable.setStatus(2);
        recordTable.setAddTime(new Date());
        ucardWalletRecordRepository.save(recordTable);

        UcardWalletWithdrawTable withdrawTable = new UcardWalletWithdrawTable();
        withdrawTable.setAddress(entity.getAddress());
        withdrawTable.setAmount(entity.getAmount());
        withdrawTable.setUniqueId(walletTable.getUniqueId());
        withdrawTable.setBlockchain(entity.getBlockchain());
        withdrawTable.setReceivedAmount(entity.getReceivedAmount());
        withdrawTable.setReceivedCurrency(entity.getReceivedCurrency());
        withdrawTable.setStatus(CardApproveStatusEnum.PENDING.getCode());
        withdrawTable.setTransactionStatus(CardWalletWithdrawStatusEnum.PENDING.getStatus());
        withdrawTable.setAddTime(new Date());
        walletWithdrawRepository.save(withdrawTable);
        // 保存操作记录
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getCId());
        operateTable.setOperateType(CardOperateTypeEnum.WALLET_WITHDRAWAL_REQUEST.getCode());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(entity.getUniqueId());
        cardOperateRepository.save(operateTable);
        // 给审核人员发邮件
        ucardEmailService.sendWalletWithdrawalReviewEmail(withdrawTable);
        return BaseResultDto.success();
    }

    /**
     * 钱包提款审核
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto walletWithdrawApprove(CardWalletWithdrawApproveEntity entity) throws Exception {
        UcardWalletWithdrawTable table = walletWithdrawRepository.findFirstById(entity.getId());
        if (table == null) {
            throw ServiceException.exception(Constants.INFO_NOT_FOUND);
        }
        if (!table.getStatus().equals(CardApproveStatusEnum.PENDING.getCode())) {
            throw new ServiceException("ucard.error.wallet.withdraw.approve.invalid", getMessage("ucard.error.wallet" +
                    ".withdraw.approve.invalid"));
        }
        if (entity.getStatus().equals(CardApproveStatusEnum.APPROVED.getCode())) {
            // 审核通过，调用三方
            createCoinTransactionRequest(table);
        } else if (entity.getStatus().equals(CardApproveStatusEnum.REJECTED.getCode())) {
            // 拒绝 退款，保存数据库
            UcardWalletTable walletTable = ucardWalletRepository.getFirstByUniqueId(table.getUniqueId());
            walletTable.setBalance(walletTable.getBalance().add(table.getAmount()));
            ucardWalletRepository.save(walletTable);

            UcardWalletRecordTable recordTable = new UcardWalletRecordTable();
            recordTable.setWalletId(walletTable.getId());
            recordTable.setCId(walletTable.getCId());
            recordTable.setUniqueId(walletTable.getUniqueId());
            recordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
            recordTable.setAmount(table.getAmount());
            recordTable.setFee(BigDecimal.ZERO);
            recordTable.setType(CardWalletRecordTypeEnum.CRYPTO_CURRENCY_TRANSFER_OUT_REFUND.getCode());
            recordTable.setStatus(2);
            recordTable.setAddTime(new Date());
            ucardWalletRecordRepository.save(recordTable);

            table.setStatus(CardApproveStatusEnum.REJECTED.getCode());
            table.setApproveUser(entity.getApproveUser());
            table.setApproveDesc(entity.getApproveDesc());
            table.setApproveTime(entity.getApproveTime());
            table.setTransactionStatus(CardWalletWithdrawStatusEnum.FAILED.getStatus());
            walletWithdrawRepository.save(table);
            // 审批拒绝发送邮件
            ucardEmailService.sendWalletWithdrawalReviewFailEmail(table, Long.valueOf(table.getApproveDesc()));
        }
        // 保存操作记录
        CardOperateTable operateTable = new CardOperateTable();
        operateTable.setOperateUser(entity.getCId());
        operateTable.setOperateType(CardOperateTypeEnum.WALLET_WITHDRAWAL_APPROVAL.getCode());
        operateTable.setStatus(Constants.SUCCESS);
        operateTable.setAddTime(new Date());
        operateTable.setUniqueId(table.getUniqueId());
        cardOperateRepository.save(operateTable);
        return BaseResultDto.success();
    }

    /**
     * 创建代币请求
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createCoinTransactionRequest(UcardWalletWithdrawTable table) throws Exception {
        VaultodyConfig config = getVaultodyConfig();
        String requestPath = getPath(config.getCreateCoinTransactionPathTemplate(), config.getVaultId(),
                table.getBlockchain(), config.getNetwork());
        VaultodyCreateCoinTransactionRequestEntity entity = new VaultodyCreateCoinTransactionRequestEntity();
        entity.setAddress(table.getAddress());
        entity.setAmount(String.valueOf(table.getReceivedAmount()));
        List<VaultodyCreateCoinTransactionRequestEntity> recipients = new ArrayList<>();
        recipients.add(entity);
        Map<String, Object> itemMap = new HashMap<>();
        itemMap.put("recipients", recipients);
        itemMap.put("note", table.getUniqueId());
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("item", itemMap);
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("data", dataMap);
        Connection.Response response = VaultodyUtil.post(paramMap, requestPath, config);
        if (response.statusCode() != 200) {
            log.error("创建代币请求失败 address: {} error: {}", table.getAddress(), response.body());
            throw new ServiceException("创建代币请求失败");
        }
        // 解析 JSON 字符串
        JSONObject jsonObject = JSON.parseObject(response.body());

//        String apiVersion = jsonObject.getString("apiVersion"); // 提取 apiVersion
        String requestId = jsonObject.getString("requestId");   // 提取 requestId

        JSONObject dataObj = jsonObject.getJSONObject("data");
        JSONObject itemObj = dataObj.getJSONObject("item"); // 拿到 item 核心对象

//        String note = itemObj.getString("note");                       // 必选：备注信息
        String totalTransactionAmount = itemObj.getString("totalTransactionAmount"); // 交易总金额
        String transactionRequestId = itemObj.getString("transactionRequestId");     // 交易请求ID
        String transactionRequestStatus = itemObj.getString("transactionRequestStatus"); // 交易状态

        List<VaultodyCreateCoinTransactionDto> recipientsList =
                itemObj.getJSONArray("recipients").toJavaList(VaultodyCreateCoinTransactionDto.class);
        if (CollectionUtils.isNotEmpty(recipientsList)) {
            VaultodyCreateCoinTransactionDto dto = recipientsList.get(0);
//            String address = dto.getAddress();
//            String amount = dto.getAmount();
            String amountUnit = dto.getAmountUnit();
            table.setAmountUnit(amountUnit);
//            String classicAddress = dto.getClassicAddress();
//            String addressTag = dto.getAddressTag();
        }
        table.setRequestId(requestId);
        table.setTransactionRequestId(transactionRequestId);
        table.setTransactionRequestStatus(transactionRequestStatus);
        table.setTotalTransactionAmount(new BigDecimal(totalTransactionAmount));
        table.setModifyTime(new Date());
        table.setTransactionStatus(CardWalletWithdrawStatusEnum.PROCESSING.getStatus());
        walletWithdrawRepository.save(table);
    }

    /**
     * 钱包提款列表
     */
    @Override
    public ResultWithPagerDto<UcardWalletWithdrawDto> getWalletWithdrawPage(UcardWalletWithdrawPageEntity entity) throws ServiceException {
        try {
            /**
             * 格式化时间
             */
            if (entity.getEndDate() != null) {
                entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
            }
            // 获取总数
            Integer count = cardWalletWithdrawMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }

            List<UcardWalletWithdrawDto> dtos = cardWalletWithdrawMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取钱包提款分页列表失败", e);
            throw new ServiceException("获取钱包提款分页列表失败");
        }
    }

    /**
     * 钱包地址分页列表
     */
    @Override
    public ResultWithPagerDto<VaultodyDepositAddressDto> getDepositAddressPage(VaultodyDepositAddressPageEntity entity) throws ServiceException {
        try {
            // 获取总数
            Integer count = walletAddressMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }

            List<VaultodyDepositAddressDto> dtos = walletAddressMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取钱包地址分页列表失败", e);
            throw new ServiceException("获取钱包地址分页列表失败");
        }
    }

    /**
     * 重置钱包地址
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseResultDto resetDepositAddress(VaultodyResetDepositAddressEntity entity) throws ServiceException {
        CardWalletAddressTable table = walletAddressRepository.findFirstById(entity.getId());
        if (table == null) {
            return BaseResultDto.error("钱包地址不存在");
        }
        if (table.getIsReset()) {
            return BaseResultDto.error("钱包地址已重置");
        }
        table.setIsReset(true);
        walletAddressRepository.save(table);
        return BaseResultDto.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void notify(String content, String xSignature) throws Exception {
        VaultodyConfig vaultodyConfig = getVaultodyConfig();
        if (!isCallbackValid(content, vaultodyConfig.getWebhooksPassphrase(), xSignature)) {
            //打印日志 详细说明
            log.error("callback valid error");
            throw new ServiceException("callback valid error");
        }
        VaultodyCallbackEntity entity = JSON.parseObject(content, VaultodyCallbackEntity.class);
        String transactionId = entity.getData().getItem().getTransactionId();
        if (StringUtils.isEmpty(transactionId)) {
            log.error("Vaultody通知回调：transactionId为空，{}", content);
            return;
        }
        // 根据idempotencyKey 查询数据表是否存在该条数据，如果存在说明已经处理过
        UcardVaultodyCallbackRecordTable callbackRecordTable =
                vaultodyCallbackRecordRepository.findFirstByTransactionId(transactionId);
        if (callbackRecordTable != null) {
            log.error("Vaultody通知回调：transactionId已存在，数据重复，{}", JSON.toJSONString(entity));
            return;
        }
        VaultodyDataEntity dataEntity = entity.getData();
        String event = dataEntity.getEvent();
        if (StringUtils.isEmpty(event)) {
            log.error("Vaultody通知回调：event为空，暂不处理，{}", JSON.toJSONString(entity));
            return;
        }
        VaultodyItemEntity itemEntity = dataEntity.getItem();
        String direction = itemEntity.getDirection();
        // 处理入账
        if ("INCOMING_MINED_TX".equals(event) && "incoming".equals(direction)) {
            handleIncomingConfirmedCoinTx(entity);
            return;
        }

        // 处理转出失败
        if ("TRANSACTION_REJECTED".equals(event) || "OUTGOING_MINED_WITH_ERRORS".equals(event)
                || "OUTGOING_FAILED".equals(event)) {
            transactionRejected(entity);
            return;
        }
        // 处理转出交易
        if ("INCOMING_CONFIRMED_TOKEN_TX".equals(event)) {
            processTheTransferTransaction(entity);
        }
    }

    /**
     * 处理转出交易
     */
    @Transactional(rollbackFor = Exception.class)
    public void processTheTransferTransaction(VaultodyCallbackEntity entity) throws ServiceException {
        VaultodyDataEntity dataEntity = entity.getData();
        VaultodyItemEntity itemEntity = dataEntity.getItem();
        UcardWalletWithdrawTable table =
                walletWithdrawRepository.findFirstByAddressAndBlockchain(itemEntity.getAddress(),
                        itemEntity.getBlockchain());
        if (table == null) {
            log.error("Vaultody通知回调：未查询到提现钱包地址数据，{}", JSON.toJSONString(itemEntity));
            throw new ServiceException("Vaultody通知回调,未查询到提现钱包地址数据");
        }
        if (StringUtils.isEmpty(itemEntity.getStatus())) {
            log.error("Vaultody通知回调：交易状态为空，{}", JSON.toJSONString(itemEntity));
            throw new ServiceException("Vaultody通知回调,交易状态为空");
        }
        if ("success".equals(itemEntity.getStatus())) {
            table.setTransactionStatus(CardWalletWithdrawStatusEnum.SUCCESS.getStatus());
        } else if ("failed".equals(itemEntity.getStatus())) {
            table.setTransactionStatus(CardWalletWithdrawStatusEnum.FAILED.getStatus());
        } else {
            log.error("Vaultody通知回调：交易状态错误，{}", JSON.toJSONString(itemEntity));
            throw new ServiceException("Vaultody通知回调,交易状态错误");
        }
        UcardVaultodyCallbackRecordTable recordTable = saveCallbackRecord(table.getUniqueId(), entity, null);
        vaultodyCallbackRecordRepository.save(recordTable);
        walletWithdrawRepository.save(table);
        if (table.getTransactionStatus().equals(CardWalletWithdrawStatusEnum.SUCCESS.getStatus())) {
            ucardEmailService.sendWalletWithdrawalSuccessEmail(table.getAmount(), BigDecimal.ZERO, new Date(),
                    table.getUniqueId());
        } else if (table.getTransactionStatus().equals(CardWalletWithdrawStatusEnum.FAILED.getStatus())) {
            ucardEmailService.sendWalletWithdrawalFailedEmail(table);
        }
    }

    /**
     * 处理交易失败
     */
    @Transactional(rollbackFor = Exception.class)
    public void transactionRejected(VaultodyCallbackEntity entity) throws ServiceException {
        VaultodyDataEntity dataEntity = entity.getData();
        VaultodyItemEntity itemEntity = dataEntity.getItem();
        UcardWalletWithdrawTable table =
                walletWithdrawRepository.findFirstByRequestIdAndAddress(itemEntity.getRequestId(),
                        itemEntity.getAddress());
        if (table == null) {
            log.error("Vaultody通知回调：未查询到提现钱包地址数据，{}", JSON.toJSONString(itemEntity));
            throw new ServiceException("Vaultody通知回调,未查询到提现钱包地址数据");
        }
        table.setTransactionStatus(CardWalletWithdrawStatusEnum.FAILED.getStatus());
        walletWithdrawRepository.save(table);
        UcardVaultodyCallbackRecordTable recordTable = saveCallbackRecord(table.getUniqueId(), entity, null);
        vaultodyCallbackRecordRepository.save(recordTable);

    }

    /**
     * 处理转入通知
     */
    @Transactional(rollbackFor = Exception.class)
    public void handleIncomingConfirmedCoinTx(VaultodyCallbackEntity entity) throws Exception {
        VaultodyDataEntity dataEntity = entity.getData();
        VaultodyItemEntity itemEntity = dataEntity.getItem();
        String address = itemEntity.getAddress();
        String blockchain = itemEntity.getBlockchain();
        // 根据地址和区块链查询用户
        CardWalletAddressTable table = cardWalletAddressRepository.getFirstByAddressAndBlockchain(address, blockchain);
        if (table == null || StringUtils.isEmpty(table.getUniqueId())) {
            log.error("Vaultody通知回调：该地址未绑定用户，{}", JSON.toJSONString(entity));
            return;
        }
        UcardWalletTable walletTable = ucardWalletRepository.getFirstByUniqueId(table.getUniqueId());
        if (walletTable == null) {
            log.error("Vaultody通知回调：用户钱包不存在，{}", JSON.toJSONString(entity));
            return;
        }
        String amount = itemEntity.getAmount();
        String unit = itemEntity.getUnit();
        String status = itemEntity.getStatus();
        if (!"success".equals(status)) {
            log.error("Vaultody通知回调：转入交易未成功，暂不处理，{}", JSON.toJSONString(entity));
            return;
        }
        BigDecimal addAmount = StringUtils.isEmpty(amount) ? BigDecimal.ZERO : new BigDecimal(amount);
        // 金额为空或负暂不处理
        if (addAmount.compareTo(BigDecimal.ZERO) <= 0) {
            log.error("Vaultody通知回调,通知金额为空，暂不处理，{}", JSON.toJSONString(entity));
            return;
        }
        // 查询币种汇率
        CardBlockchainRateConfigTable rateConfigTable =
                blockchainRateConfigRepository.getByCurrencyAndConvertCurrencyAndEnableStatus("USDT", unit, 1);
        if (rateConfigTable == null || rateConfigTable.getExchangeRate() == null) {
            log.error("Vaultody通知回调,币种汇率不存在，{}", JSON.toJSONString(entity));
            throw new ServiceException("Vaultody通知回调,币种汇率不存在");
        }
        addAmount = addAmount.multiply(rateConfigTable.getExchangeRate()).setScale(2, RoundingMode.HALF_UP);
        BigDecimal balance = walletTable.getBalance().add(addAmount);
        walletTable.setBalance(balance);
        ucardWalletRepository.save(walletTable);

        UcardWalletRecordTable recordTable = new UcardWalletRecordTable();
        recordTable.setWalletId(walletTable.getId());
        recordTable.setCId(walletTable.getCId());
        recordTable.setUniqueId(walletTable.getUniqueId());
        recordTable.setSerial(GetRandom.getOrderNo(PayConstant.UCARD_HEAD));
        recordTable.setAmount(addAmount);
        recordTable.setFee(BigDecimal.ZERO);
        recordTable.setType(CardWalletRecordTypeEnum.CRYPTO_CURRENCY_TRANSFER.getCode());
        recordTable.setStatus(2);
        recordTable.setAddTime(new Date());
        ucardWalletRecordRepository.save(recordTable);
        // 保存记录信息
        UcardVaultodyCallbackRecordTable callbackRecordTable = saveCallbackRecord(table.getUniqueId(), entity,
                addAmount);
        vaultodyCallbackRecordRepository.save(callbackRecordTable);
        // 发邮件给客户通知到账邮件
        ucardEmailService.sendEncryptedWalletSuccessEmail(callbackRecordTable);

    }

    @Transactional(rollbackFor = Exception.class)
    public UcardVaultodyCallbackRecordTable saveCallbackRecord(String uniqueId, VaultodyCallbackEntity entity,
                                                               BigDecimal addAmount) {
        VaultodyDataEntity dataEntity = entity.getData();
        VaultodyItemEntity itemEntity = dataEntity.getItem();
        VaultodyMinedInBlockEntity blockEntity = itemEntity.getMinedInBlock();
        UcardVaultodyCallbackRecordTable callbackRecordTable = new UcardVaultodyCallbackRecordTable();
        BeanUtils.copyProperties(entity, callbackRecordTable);
        BeanUtils.copyProperties(dataEntity, callbackRecordTable);
        BeanUtils.copyProperties(itemEntity, callbackRecordTable);
        BeanUtils.copyProperties(blockEntity, callbackRecordTable);

        callbackRecordTable.setUniqueId(uniqueId);
        callbackRecordTable.setReceivedAmount(addAmount);
        callbackRecordTable.setTimestamp(new Date(blockEntity.getTimestamp() * 1000));
        callbackRecordTable.setAddTime(new Date());
        return callbackRecordTable;
    }

    /**
     * 加密货币交易分页列表
     */
    @Override
    public ResultWithPagerDto<UcardWalletDepositTransactionDto> getWalletDepositTransactionPage(UcardWalletDepositTransactionPageEntity entity) throws ServiceException {
        try {
            /**
             * 格式化时间
             */
            if (entity.getEndDate() != null) {
                entity.setEndDate(DateUtil.operationDay(entity.getEndDate(), 1));
            }
            // 获取总数
            Integer count = cryptoCallbackRecordMapper.countList(entity);
            PageDto pageDto = PageDto.format(entity, count);
            if (count == null || count <= 0) {
                return ResultWithPagerDto.success(pageDto);
            }

            List<UcardWalletDepositTransactionDto> dtos = cryptoCallbackRecordMapper.pageList(entity);
            return ResultWithPagerDto.success(pageDto, dtos);
        } catch (Exception e) {
            log.error("获取加密货币交易分页列表失败", e);
            throw new ServiceException("获取加密货币交易分页列表失败");
        }
    }

    private boolean isCallbackValid(String message, String apiSecret, String signature) {
        String _signature = HAMCSHA256Util.sha256_HMAC(message, apiSecret);

        //打印签名 两个签名数据和比对结果
        log.info("isCallbackValid,_signature：{}，signature：{}", _signature, signature);

        return _signature.equals(signature);
    }
}
