package io.nuls.crosschain.nuls.servive.impl;

import io.nuls.base.RPCUtil;
import io.nuls.base.basic.AddressTool;
import io.nuls.base.data.BlockExtendsData;
import io.nuls.base.data.BlockHeader;
import io.nuls.base.data.NulsHash;
import io.nuls.base.data.Transaction;
import io.nuls.core.basic.Result;
import io.nuls.core.constant.TxType;
import io.nuls.core.core.annotation.Autowired;
import io.nuls.core.core.annotation.Component;
import io.nuls.crosschain.base.constant.CommandConstant;
import io.nuls.crosschain.base.message.BroadCtxHashMessage;
import io.nuls.crosschain.base.model.bo.ChainInfo;
import io.nuls.crosschain.base.model.bo.txdata.VerifierChangeData;
import io.nuls.crosschain.base.model.bo.txdata.VerifierInitData;
import io.nuls.crosschain.nuls.constant.NulsCrossChainConfig;
import io.nuls.crosschain.nuls.constant.NulsCrossChainConstant;
import io.nuls.crosschain.nuls.constant.ParamConstant;
import io.nuls.crosschain.nuls.model.bo.BroadFailFlag;
import io.nuls.crosschain.nuls.model.bo.Chain;
import io.nuls.crosschain.nuls.model.po.SendCtxHashPO;
import io.nuls.crosschain.nuls.model.po.VerifierChangeSendFailPO;
import io.nuls.crosschain.nuls.rpc.call.ConsensusCall;
import io.nuls.crosschain.nuls.rpc.call.NetWorkCall;
import io.nuls.crosschain.nuls.servive.BlockService;
import io.nuls.crosschain.nuls.srorage.*;
import io.nuls.crosschain.nuls.utils.BroadCtxUtil;
import io.nuls.crosschain.nuls.utils.MessageUtil;
import io.nuls.crosschain.nuls.utils.TxUtil;
import io.nuls.crosschain.nuls.utils.manager.ChainManager;
import io.nuls.crosschain.nuls.utils.thread.VerifierChangeTxHandler;

import java.util.*;

import static io.nuls.crosschain.nuls.constant.NulsCrossChainErrorCode.*;
import static io.nuls.crosschain.nuls.constant.ParamConstant.CHAIN_ID;
import static io.nuls.crosschain.nuls.constant.ParamConstant.NEW_BLOCK_HEIGHT;

/**
 * 提供给区块模块调用的接口实现类
 * @author tag
 * @date 2019/4/25
 */
@Component
public class BlockServiceImpl implements BlockService {
    @Autowired
    private ChainManager chainManager;

    @Autowired
    private SendHeightService sendHeightService;

    @Autowired
    private SendedHeightService sendedHeightService;

    @Autowired
    private NulsCrossChainConfig config;

    @Autowired
    private ConvertCtxService convertCtxService;

    @Autowired
    private CtxStatusService ctxStatusService;

    @Autowired
    private VerifierChangeBroadFailedService verifierChangeBroadFailedService;

    @Override
    @SuppressWarnings("unchecked")
    public Result syncStatusUpdate(Map<String, Object> params) {
        if (params.get(CHAIN_ID) == null || params.get(ParamConstant.SYNC_STATUS) == null) {
            return Result.getFailed(PARAMETER_ERROR);
        }
        int chainId = (int) params.get(CHAIN_ID);
        if (chainId <= 0) {
            return Result.getFailed(PARAMETER_ERROR);
        }
        Chain chain = chainManager.getChainMap().get(chainId);
        if (chain == null) {
            return Result.getFailed(CHAIN_NOT_EXIST);
        }
        int syncStatus = (int)params.get(ParamConstant.SYNC_STATUS);
        chain.setSyncStatus(syncStatus);
        chain.getLogger().info("节点同步状态变更，syncStatus:{}",syncStatus );
        return Result.getSuccess(SUCCESS);
    }

    @Override
    @SuppressWarnings("unchecked")
    public Result newBlockHeight(Map<String, Object> params) {
        Result result = paramValid(params);
        if(result.isFailed()){
            return result;
        }
        int chainId = (int) params.get(CHAIN_ID);
        Chain chain = chainManager.getChainMap().get(chainId);
        long height = Long.valueOf(params.get(NEW_BLOCK_HEIGHT).toString());
        chain.getLogger().info("收到区块高度更新信息，最新区块高度为：{}", height);
        //查询是否有待广播的跨链交易
        Map<Long , SendCtxHashPO> sendHeightMap = sendHeightService.getList(chainId);
        if(sendHeightMap != null && sendHeightMap.size() >0){
            Set<Long> sortSet = new TreeSet<>(sendHeightMap.keySet());
            //各条链状态缓存
            Map<Integer,Byte> crossStatusMap = new HashMap<>(NulsCrossChainConstant.INIT_CAPACITY_16);
            //广播到各链失败的各种交易类型缓存，避免广播乱序
            Map<Integer, BroadFailFlag> broadFailMap = new HashMap<>(NulsCrossChainConstant.INIT_CAPACITY_16);
            for (long cacheHeight:sortSet) {
                if(height >= cacheHeight){
                    chain.getLogger().debug("广播区块高度为{}的跨链交易给其他链",cacheHeight );
                    SendCtxHashPO po = sendHeightMap.get(cacheHeight);
                    List<NulsHash> broadSuccessCtxHash = new ArrayList<>();
                    List<NulsHash> broadFailCtxHash = new ArrayList<>();
                    for (NulsHash ctxHash:po.getHashList()) {
                        if(BroadCtxUtil.broadCtxHash(chain, ctxHash, cacheHeight, crossStatusMap,broadFailMap)){
                            broadSuccessCtxHash.add(ctxHash);
                        }else{
                            broadFailCtxHash.add(ctxHash);
                        }
                    }
                    if(broadSuccessCtxHash.size() > 0){
                        SendCtxHashPO sendedPo = sendedHeightService.get(cacheHeight,chainId);
                        if(sendedPo != null){
                            sendedPo.getHashList().addAll(broadSuccessCtxHash);
                        }else{
                            sendedPo = new SendCtxHashPO(broadSuccessCtxHash);
                        }
                        if(!sendedHeightService.save(cacheHeight, sendedPo, chainId)){
                            continue;
                        }
                    }
                    if(broadFailCtxHash.size() > 0){
                        po.setHashList(broadFailCtxHash);
                        sendHeightService.save(cacheHeight, po, chainId);
                        chain.getLogger().error("区块高度为{}的跨链交易广播失败",cacheHeight);
                    }else{
                        sendHeightService.delete(cacheHeight, chainId);
                        chain.getLogger().info("区块高度为{}的跨链交易广播成功",cacheHeight);
                    }
                }else{
                    break;
                }
            }
        }
        chain.getLogger().debug("区块高度更新消息处理完成,Height:{}\n\n",height);
        return Result.getSuccess(SUCCESS);
    }

    private Result paramValid(Map<String, Object> params){
        if (params.get(CHAIN_ID) == null || params.get(NEW_BLOCK_HEIGHT) == null || params.get(ParamConstant.PARAM_BLOCK_HEADER) == null) {
            return Result.getFailed(PARAMETER_ERROR);
        }
        int chainId = (int) params.get(CHAIN_ID);
        if (chainId <= 0) {
            return Result.getFailed(PARAMETER_ERROR);
        }
        Chain chain = chainManager.getChainMap().get(chainId);
        if (chain == null) {
            return Result.getFailed(CHAIN_NOT_EXIST);
        }
        try {
            BlockHeader blockHeader = new BlockHeader();
            String headerHex = (String) params.get(ParamConstant.PARAM_BLOCK_HEADER);
            blockHeader.parse(RPCUtil.decode(headerHex), 0);
            if(!chainManager.isCrossNetUseAble()){
                chainManager.getChainHeaderMap().put(chainId, blockHeader);
                chain.getLogger().info("等待共识网络组网完成");
                return Result.getSuccess(SUCCESS);
            }
            if(config.isMainNet() && chainManager.getRegisteredCrossChainList().size() <= 1){
                chain.getLogger().info("当前没有注册链" );
                chainManager.getChainHeaderMap().put(chainId, blockHeader);
                return Result.getSuccess(SUCCESS);
            }
            /*
            检测是否有轮次变化，如果有轮次变化，查询共识模块共识节点是否有变化，如果有变化则创建验证人变更交易(该操作需要在验证人初始化交易之后)
            */
            if(chain.getVerifierList() != null && !chain.getVerifierList().isEmpty()){
                Map<String,List<String>> agentChangeMap;
                BlockHeader localHeader = chainManager.getChainHeaderMap().get(chainId);
                if(localHeader != null){
                    BlockExtendsData blockExtendsData = blockHeader.getExtendsData();
                    BlockExtendsData localExtendsData = localHeader.getExtendsData();
                    if(blockExtendsData.getRoundIndex() == localExtendsData.getRoundIndex()){
                        chainManager.getChainHeaderMap().put(chainId, blockHeader);
                        return Result.getSuccess(SUCCESS);
                    }
                    agentChangeMap = ConsensusCall.getAgentChangeInfo(chain, localHeader.getExtend(), blockHeader.getExtend());
                }else{
                    agentChangeMap = ConsensusCall.getAgentChangeInfo(chain, null, blockHeader.getExtend());
                }
                if(agentChangeMap != null){
                    List<String> registerAgentList = agentChangeMap.get(ParamConstant.PARAM_REGISTER_AGENT_LIST);
                    List<String> cancelAgentList = agentChangeMap.get(ParamConstant.PARAM_CANCEL_AGENT_LIST);
                    //第一个区块特殊处理,判断获取的到的变更的验证人列表是否正确
                    if(localHeader == null){
                        if(registerAgentList != null){
                            registerAgentList.removeAll(chain.getVerifierList());
                        }
                    }
                    boolean verifierChange = (registerAgentList != null && !registerAgentList.isEmpty()) || (cancelAgentList != null && !cancelAgentList.isEmpty());
                    if(verifierChange){
                        chain.getLogger().info("有验证人变化，创建验证人变化交易，最新轮次与上一轮共有的出块地址为：{},新增的验证人列表：{},减少的验证人列表：{}", chain.getVerifierList().toString(),registerAgentList,cancelAgentList);
                        Transaction verifierChangeTx = TxUtil.createVerifierChangeTx(registerAgentList, cancelAgentList, blockHeader.getExtendsData().getRoundStartTime(),chainId);
                        chain.getCrossTxThreadPool().execute(new VerifierChangeTxHandler(chain, verifierChangeTx, blockHeader.getHeight()));
                    }
                }
            }
            chainManager.getChainHeaderMap().put(chainId, blockHeader);
        }catch (Exception e){
            chain.getLogger().error(e);
            return Result.getFailed(DATA_PARSE_ERROR);
        }
        return Result.getSuccess(SUCCESS);
    }
}
