package com.fzm.mall.server.admin.chain;

import cn.fzm.chain.simplesdk.client.ParaChainClient;
import cn.fzm.chain.simplesdk.constant.TokenStatusEnum;
import cn.fzm.chain.simplesdk.constant.TxStatusEnum;
import cn.fzm.chain.simplesdk.model.TxResult;
import com.alibaba.fastjson.JSONObject;
import com.fzm.mall.server.admin.constant.MallResponseError;
import com.fzm.mall.server.admin.exception.MyException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wangp
 * @date 2021/3/10 17:16
 * @description 区块链
 * @since JDK 1.8
 */
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FzmChain {

    private final ParaChainClient paraChainClient;

    @Value("${chain.para.token-manager}")
    private String tokenManager;
    @Value("${chain.para.token-manager-key}")
    private String tokenManagerKey;

    /**
     * 创建账户
     *
     * @param label 账户标签
     * @return 账户地址
     */
    public String accountCreate(String label) {
        String address = paraChainClient.walletCreateAccount(label);
        if (address == null) {
            log.error("TOKEN地址创建失败，label:{}", label);
            throw new MyException(MallResponseError.ADDRESS_ERROR);
        }
        return address;
    }

    /**
     * 创建地址私钥
     *
     * @param address 账户地址
     * @return 地址私钥
     */
    public String addressPriKey(String address) {
        String key = paraChainClient.walletDumpPrivkey(address);
        if (key == null) {
            log.error("TOKEN私钥导出失败，label:{}", address);
            throw new MyException(MallResponseError.PRI_KEY_ERROR);
        }
        return key;
    }

    /**
     * 判断商品币是否存在
     *
     * @param coin 币名称
     * @return T F
     */
    public boolean coinExist(String coin) {
        boolean existPre = paraChainClient.tokenExisted(coin, TokenStatusEnum.PREOK);
        boolean existFin = paraChainClient.tokenExisted(coin, TokenStatusEnum.FINOK);
        if (existPre || existFin) {
            log.error("TOKEN发行失败，失败原因：TOKEN已存在，label:{}", coin);
        }
        return existPre || existFin;
    }

    /**
     * 发行TOKEN
     *
     * @param coin      名称
     * @param amount    发行数量
     * @param tokenAddr 地址
     * @return T F
     */
    public Map<String, String> IssueCoin(String coin, int amount, String tokenAddr) {
        Map<String, String> map = new HashMap<>();
        map.put("status", "SUCCESS");
        map.put("errMsg", "");

        //进行预发售
        BigDecimal totalAmt = new BigDecimal(amount);
        String hash = paraChainClient.paraTokenCreatePre(coin, coin, totalAmt, tokenAddr, tokenManager, tokenManagerKey);
        TxResult tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("TOKEN预发行失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            map.put("status", "FAIL");
            map.put("errMsg", tr.getErrMsg().getValue());
            return map;
        }

        //完成发行
        hash = paraChainClient.paraTokenCreateFin(coin, tokenAddr, tokenManager, tokenManagerKey);
        tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("TOKEN完成发行失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            map.put("status", "FAIL");
            map.put("errMsg", tr.getErrMsg().getValue());
        }
        return map;
    }

    /**
     * 增发TOKEN
     *
     * @param coin   名称
     * @param addr   地址
     * @param key    私钥
     * @param amount 增发数量
     * @return T F
     */
    public Map<String, String> coinMint(String coin, String addr, String key, int amount) {
        Map<String, String> map = new HashMap<>();
        map.put("status", "SUCCESS");
        map.put("errMsg", "");

        BigDecimal totalAmt = new BigDecimal(amount);
        String hash = paraChainClient.paraTokenMint(coin, addr, key, totalAmt);
        TxResult tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("TOKEN增发失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            map.put("status", "FAIL");
            map.put("errMsg", tr.getErrMsg().getValue());
        }
        return map;
    }

    /**
     * 增发TOKEN hash
     *
     * @param coin   名称
     * @param addr   地址
     * @param key    私钥
     * @param amount 增发数量
     * @return T F
     */
    public String coinMintHash(String coin, String addr, String key, int amount) {
        return paraChainClient.paraTokenMint(coin, addr, key, new BigDecimal(amount));
    }

    /**
     * 减发TOKEN
     *
     * @param coin   名称
     * @param addr   地址
     * @param key    私钥
     * @param amount 减发数量
     * @return T F
     */
    public Map<String, String> coinBurn(String coin, String addr, String key, int amount) {
        Map<String, String> map = new HashMap<>();
        map.put("status", "SUCCESS");
        map.put("errMsg", "");

        BigDecimal totalAmt = new BigDecimal(amount);
        String hash = paraChainClient.paraTokenBurn(coin, addr, key, totalAmt);
        TxResult tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("TOKEN减发失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            map.put("status", "FAIL");
            map.put("errMsg", tr.getErrMsg().getValue());
        }
        return map;
    }

    /**
     * token转账
     *
     * @param fromAddr    转出账户
     * @param fromPriKey  账户私钥
     * @param toAddr      转入地址
     * @param amount      转账数量
     * @param tokenSymbol 币名
     * @return
     */
    public Map<String, String> tranToken(String fromAddr, String fromPriKey, String toAddr, BigDecimal amount, String tokenSymbol) {
        Map<String, String> map = new HashMap<>();
        map.put("status", "SUCCESS");
        map.put("errMsg", "");

        String hash = paraChainClient.paraTranToken(fromAddr, fromPriKey, toAddr, amount, tokenSymbol);
        TxResult tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
//        TxResult tr = mainChainClient.cycleConfirmTxWithHash(hash);
        System.err.println("tr:" + tr.getStatus());
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("TOKEN转账失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            map.put("status", "FAIL");
            map.put("errMsg", "tr.getErrMsg().getValue()");
        }
        return map;
    }

    /**
     * coin转账
     *
     * @param fromAddr   转出账户
     * @param fromPriKey 账户私钥
     * @param toAddr     转入地址
     * @param amount     转行数量
     * @return
     */
    public Map<String, String> tranCoin(String fromAddr, String fromPriKey, String toAddr, BigDecimal amount) {
        Map<String, String> map = new HashMap<>();
        map.put("status", "SUCCESS");
        map.put("errMsg", "");

        String hash = paraChainClient.paraTranCoin(fromAddr, fromPriKey, toAddr, amount);
        TxResult tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
//        TxResult tr = mainChainClient.confirmTxWithRealTxHash(hash);
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("Coin转账失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            map.put("status", "FAIL");
            map.put("errMsg", tr.getErrMsg().getValue());
        }
        return map;
    }

    /**
     * token转账hash
     *
     * @param fromAddr   转出账户
     * @param fromPriKey 账户私钥
     * @param toAddr     转入地址
     * @param amount     转行数量
     * @return
     */
    public String tranTokenHash(String fromAddr, String fromPriKey, String toAddr, BigDecimal amount, String tokenSymbol) {
        return paraChainClient.paraTranToken(fromAddr, fromPriKey, toAddr, amount, tokenSymbol);
    }

    /**
     * 上链结果单次确认
     *
     * @param hash 交易hash
     * @return
     */
    public TxResult confirmTxWithGrpTxHash(String hash) {
        return paraChainClient.confirmTxWithGrpTxHash(hash);
    }

    /**
     * 上链结果轮询确认
     *
     * @param hash 交易hash
     * @return
     */
    public JSONObject cycleConfirmTxWithHash(String hash) {
        JSONObject json = new JSONObject();
        json.put("status", "SUCCESS");
        json.put("errMsg", "");
        TxResult tr = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
        if (tr.getStatus() != TxStatusEnum.SUCCESS) {
            log.error("Coin转账失败，交易HASH:{},失败原因：{}", hash, tr.getErrMsg());
            json.put("status", "FAIL");
            json.put("errMsg", tr.getErrMsg().getValue());
        }
        return json;
    }

}
