package com.izouma.nineth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alipay.mychain.sdk.api.utils.Utils;
import com.alipay.mychain.sdk.domain.transaction.LogEntry;
import com.antfinancial.mychain.baas.tool.restclient.RestClient;
import com.antfinancial.mychain.baas.tool.restclient.RestClientProperties;
import com.antfinancial.mychain.baas.tool.restclient.model.CallRestBizParam;
import com.antfinancial.mychain.baas.tool.restclient.model.Method;
import com.antfinancial.mychain.baas.tool.restclient.model.ReceiptDecoration;
import com.antfinancial.mychain.baas.tool.restclient.response.BaseResp;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.config.Constants;
import com.izouma.nineth.dto.NFT;
import com.izouma.nineth.dto.NFTAccount;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.utils.HashUtils;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.math.BigInteger;

@Service
@Slf4j
@AllArgsConstructor
public class NFTService {
    private final RestClient           restClient;
    private final RestClientProperties restClientProperties;
    private final GeneralProperties    generalProperties;

    public NFTAccount createAccount(String username) {
        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
                .bizid(Constants.bizId)
                .account(restClientProperties.getAccount())
                .mykmsKeyId(restClientProperties.getKmsId())
                .newAccountId(username)
                .newAccountKmsId(Constants.kmsKey)
                .method(Method.TENANTCREATEACCUNT)
                .gas(100000L).build();
        try {
            BaseResp baseResp = restClient.chainCallForBiz(callRestBizParam);
            NFTAccount account = new NFTAccount(username, Constants.kmsKey, baseResp.getData());
            if (baseResp.isSuccess()) {
                log.info("创建账户成功 {}", account);
                return account;
            } else {
                throw new RuntimeException(baseResp.getCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("创建账户失败", e);
            throw new BusinessException("创建账户失败");
        }
    }

    @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
    public NFT createToken(String toAccount) throws Exception {
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(Utils.getIdentityByName(toAccount));
        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
                .bizid(restClientProperties.getBizid())
                .account(restClientProperties.getAccount())
                .contractName(generalProperties.getContractName())
                .methodSignature("mint(identity)")
                .inputParamListStr(jsonArray.toJSONString())
                .outTypes("[]")//合约返回值类型
                .mykmsKeyId(restClientProperties.getKmsId())
                .method(Method.CALLCONTRACTBIZASYNC)
                .tenantid(restClientProperties.getTenantid())
                .gas(500000L)
                .build();
        BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
        if (!resp.isSuccess()) {
            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
        }
        if ("200".equals(resp.getCode())) {
            log.info("EVM合约执行成功");
            // 合约调用交易回执内容
            ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
            BigInteger gasUsed = txReceipt.getGasUsed();
            long result = txReceipt.getResult();
            log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
            for (LogEntry logEntry : txReceipt.getLogs()) {
                if (logEntry.getTopics().get(0).equals(HashUtils.Keccak256("Transfer(identity,identity,uint256)"))) {
                    String tokenId = logEntry.getTopics().get(3);
                    txReceipt.getBlockNumber();
                    NFT nft = new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
                    log.info("NFT生成成功 {}", nft);
                    return nft;
                }
            }
        } else {
            // 异步交易未成功需要根据状态码判断交易状态
            log.error("EVM合约执行未成功: " + resp.getCode());
        }
        throw new BusinessException("创建nft失败");
    }


    public NFT transferToken(String tokenId, String fromAccount, String toAccount) throws Exception {
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(Utils.getIdentityByName(fromAccount));
        jsonArray.add(Utils.getIdentityByName(toAccount));
        jsonArray.add(Long.parseLong(tokenId, 16));

        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
                .bizid(restClientProperties.getBizid())
                .account(restClientProperties.getAccount())
                .contractName(generalProperties.getContractName())
                .methodSignature("transferFrom(identity,identity,uint256)")
                .inputParamListStr(jsonArray.toJSONString())
                .outTypes("[]")//合约返回值类型
                .mykmsKeyId(restClientProperties.getKmsId())
                .method(Method.CALLCONTRACTBIZASYNC)
                .tenantid(restClientProperties.getTenantid())
                .gas(500000L)
                .build();
        BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
        if (!resp.isSuccess()) {
            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
        }
        if ("200".equals(resp.getCode())) {
            log.info("EVM合约执行成功");
            // 合约调用交易回执内容
            ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
            BigInteger gasUsed = txReceipt.getGasUsed();
            long result = txReceipt.getResult();
            log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
            for (LogEntry logEntry : txReceipt.getLogs()) {
                if (logEntry.getTopics().get(0).equals(HashUtils.Keccak256("Transfer(identity,identity,uint256)"))) {
                    String transferTokenId = logEntry.getTopics().get(3);
                    txReceipt.getBlockNumber();
                    NFT nft = new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
                    log.info("NFT转移成功 {}", nft);
                    return nft;
                }
            }
        } else {
            // 异步交易未成功需要根据状态码判断交易状态
            log.error("EVM合约执行未成功: " + resp.getCode());
        }
        throw new BusinessException("创建nft失败");
    }

    public NFT setApprovalForAll(String account) throws Exception {
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(Utils.getIdentityByName(account));
        jsonArray.add(true);

        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
                .orderId(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()))
                .bizid(restClientProperties.getBizid())
                .account(restClientProperties.getAccount())
                .contractName(generalProperties.getContractName())
                .methodSignature("setApprovalForAll(identity,bool)")
                .inputParamListStr(jsonArray.toJSONString())
                .outTypes("[]")//合约返回值类型
                .mykmsKeyId(restClientProperties.getKmsId())
                .method(Method.CALLCONTRACTBIZASYNC)
                .tenantid(restClientProperties.getTenantid())
                .gas(500000L)
                .build();
        BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
        if (!resp.isSuccess()) {
            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
        }
        if ("200".equals(resp.getCode())) {
            log.info("EVM合约执行成功");
            // 合约调用交易回执内容
            ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
            BigInteger gasUsed = txReceipt.getGasUsed();
            long result = txReceipt.getResult();
            log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
        } else {
            // 异步交易未成功需要根据状态码判断交易状态
            log.error("EVM合约执行未成功: " + resp.getCode());
        }
        throw new BusinessException("创建nft失败");
    }

    public void deployContract(String name, String code) throws Exception {
        String orderId = "order_" + System.currentTimeMillis();
        CallRestBizParam callRestBizParam = CallRestBizParam.builder()
                .orderId(orderId)
                .bizid(restClientProperties.getBizid())
                .account(restClientProperties.getAccount())
                .contractName(name)
                .contractCode(code)
                .mykmsKeyId(restClientProperties.getKmsId())
                .method(Method.DEPLOYCONTRACTFORBIZ)
                .tenantid(restClientProperties.getTenantid())
                .gas(1000000L).build();
        BaseResp resp = restClient.chainCallForBiz(callRestBizParam);
        if (!resp.isSuccess()) {
            log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
        }
        log.info("EVM合约执行成功");
        // 合约调用交易回执内容
        ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
        BigInteger gasUsed = txReceipt.getGasUsed();
        long result = txReceipt.getResult();
        log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
    }

}
