package cn.iocoder.yudao.module.chain.service.blockcertificate;

import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.enums.CodeEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.chain.controller.admin.blockcertificate.vo.*;
import cn.iocoder.yudao.module.chain.convert.blockcertificate.BlockCertificateConvert;
import cn.iocoder.yudao.module.chain.dal.dataobject.blockcertificate.BlockCertificateDO;
import cn.iocoder.yudao.module.chain.dal.dataobject.operatedetail.OperateDetailDO;
import cn.iocoder.yudao.module.chain.dal.mysql.blockcertificate.BlockCertificateMapper;
import cn.iocoder.yudao.module.chain.dal.mysql.operatedetail.OperateDetailMapper;
import cn.iocoder.yudao.module.chain.enums.ChainStateEnum;
import cn.iocoder.yudao.module.chain.enums.ChainStatusEnum;
import cn.iocoder.yudao.module.chain.enums.ErrorCodeConstants;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 区块证书 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class BlockCertificateServiceImpl implements BlockCertificateService {

    @Resource
    private BlockCertificateMapper blockCertificateMapper;

    @Resource
    private OperateDetailMapper operateDetailMapper;

    @Override
    @Transactional
    public List<BlockCertificateDO> createBlockCertificate(List<BlockCertificateCreateReqVO> createReqs) {
        if(createReqs.isEmpty()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PARAMS_IS_ERROR);
        }
        List<BlockCertificateDO> blockCertificates = new ArrayList<>();
        List<OperateDetailDO> operateDetails = new ArrayList<>();
        createReqs.forEach(createReq -> {
            BlockCertificateDO blockCertificateDO = new BlockCertificateDO();
            BeanUtil.copyProperties(createReq, blockCertificateDO);
            if(blockCertificateDO.getChainOperateId() == null) {
                blockCertificateDO.setChainOperateId(CodeEnum.CHAIN_OPERATE_CODE.getCode());
            }
            if(blockCertificateDO.getChainState().equals(ChainStateEnum.ON_CHAIN.getState())) {
                blockCertificateDO.setChainBlockIdentity(StrUtils.getRandomUpperString(48).toLowerCase());
                blockCertificateDO.setBlockTime(new Date());
            }
            createReq.getDetails().forEach(detailReq -> {
                OperateDetailDO operateDetailDO = new OperateDetailDO();
                BeanUtil.copyProperties(detailReq, operateDetailDO);
                operateDetailDO.setChainOperateId(blockCertificateDO.getChainOperateId());
                operateDetails.add(operateDetailDO);
            });
            blockCertificates.add(blockCertificateDO);
        });

        // 插入主表
        blockCertificateMapper.insertBatch(blockCertificates);

        // 插入明细表
        operateDetailMapper.insertBatch(operateDetails);

        return blockCertificates;
    }

    @Override
    public void updateBlockCertificate(BlockCertificateUpdateReqVO updateReqVO) {
        // 校验存在
        validateBlockCertificateExists(updateReqVO.getId());
        // 更新
        BlockCertificateDO updateObj = BlockCertificateConvert.INSTANCE.convert(updateReqVO);
        blockCertificateMapper.updateById(updateObj);
    }

    @Override
    @Transactional
    public void updateBlockCertificateBatch(List<BlockCertificateUpdateReqVO> updateReqVOS) {
        // 校验存在
        List<Long> ids = updateReqVOS.stream().map(BlockCertificateUpdateReqVO::getId).collect(Collectors.toList());
        this.validateBatch(ids);
        List<BlockCertificateDO> updateObjs = BlockCertificateConvert.INSTANCE.convertToDOList(updateReqVOS);
        List<OperateDetailDO> operateDetails = new ArrayList<>();
        updateReqVOS.forEach(updateReq -> updateReq.getDetails().forEach(detail -> {
            OperateDetailDO operateDetail = new OperateDetailDO();
            BeanUtil.copyProperties(detail, operateDetail);
            operateDetails.add(operateDetail);
        }));

        // 更新主表
        if(!updateObjs.isEmpty()) {
            blockCertificateMapper.updateBatch(updateObjs, Integer.MAX_VALUE);
        }

        // 更新明细表
        if(!operateDetails.isEmpty()) {
            operateDetailMapper.updateBatch(operateDetails, Integer.MAX_VALUE);
        }
    }

    @Override
    public void deleteBlockCertificate(Long id) {
        // 校验存在
        BlockCertificateDO blockCertificateDO = this.validateBlockCertificateExists(id);
        if(blockCertificateDO.getChainState().equals(ChainStateEnum.ON_CHAIN.getState())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CAN_NOT_DELETE_WHEN_ON_CHAIN);
        }
        // 删除
        blockCertificateMapper.deleteById(id);
    }

    @Override
    public void deleteBlockCertificateBatch(List<Long> ids) {
        // 校验存在
        this.validateBatch(ids);
        blockCertificateMapper.deleteBatchIds(ids);
    }

    private void validateBatch(List<Long> ids) {
        List<BlockCertificateDO> BlockCertificates = blockCertificateMapper.selectList(new LambdaQueryWrapperX<BlockCertificateDO>().in(BlockCertificateDO::getId, ids));
        if(BlockCertificates.size() != ids.size()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.BLOCK_CERTIFICATE_NOT_EXISTS);
        }
    }

    private BlockCertificateDO validateBlockCertificateExists(Long id) {
        BlockCertificateDO blockCertificateDO = blockCertificateMapper.selectById(id);
        if (blockCertificateDO == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.BLOCK_CERTIFICATE_NOT_EXISTS);
        }
        return blockCertificateDO;
    }

    /**
     * 根据条件获得区块证书，此接口只支持单个
     * @param blockCertificate 区块证书 Request VO
     * @return 区块证书集合
     */
    @Override
    public List<BlockCertificateRespVO> getBlockCertificateList(BlockCertificateReqVO blockCertificate) {
//        if(blockCertificate.getId() == null && blockCertificate.getChainOperateId() == null && blockCertificate.getOperateType() == null
//            && blockCertificate.getBatchId() == null && blockCertificate.getBatchType() == null) {
//            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PARAMS_IS_ERROR);
//        }
        return blockCertificateMapper.getBlockCertificateList(blockCertificate);
    }

    @Override
    public PageResult<BlockCertificateDO> getBlockCertificatePage(BlockCertificatePageReqVO pageReqVO) {
        return blockCertificateMapper.selectPage(pageReqVO);
    }

    @Override
    public List<BlockCertificateRespVO> createAndUpChainBatch(BlockCertificateUpChainReqVO createReqVO) {
        List<Long> objReqIds = createReqVO.getOperateObjs().stream().map(UpChainObjReqVO::getOperateObjId).collect(Collectors.toList());
        // 校验是否已经创建过(简单校验)
        List<BlockCertificateDO> blockCertificates = blockCertificateMapper.selectList(new LambdaQueryWrapperX<BlockCertificateDO>().eq(BlockCertificateDO::getOperateType, createReqVO.getOperateType())
            .in(BlockCertificateDO::getOperateObjId, objReqIds).eqIfPresent(BlockCertificateDO::getBatchType, createReqVO.getBatchType()));
        if(!blockCertificates.isEmpty()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.BLOCK_CERTIFICATE_HAS_EXISTS);
        }

        Date now = new Date();
        List<BlockCertificateDO> insertDOS = new ArrayList<>();
        createReqVO.getOperateObjs().forEach(item -> {
            String code = StrUtils.getRandomUpperString(48).toLowerCase();
            BlockCertificateDO blockCertificate = new BlockCertificateDO();
            // 因为是内部接口  这里不再多做另外校验， 自己保证operateObjId的正确性（而且这里额外校验会比较麻烦
            BeanUtil.copyProperties(createReqVO, blockCertificate);

            if(item.getBatchId() != null) {
                blockCertificate.setBatchId(item.getBatchId());
            }
            blockCertificate.setOperateObjId(item.getOperateObjId());
            blockCertificate.setChainOperateId(CodeEnum.CHAIN_OPERATE_CODE.getCode());
            // 设置为上链
            blockCertificate.setChainState(ChainStateEnum.ON_CHAIN.getState());
            blockCertificate.setChainStatus(ChainStatusEnum.ACTIVE.getStatus());
            blockCertificate.setLinkId(CodeEnum.TRACE_LINK_CODE.getCode());
            blockCertificate.setScanShow(Boolean.TRUE);
            blockCertificate.setChainBlockIdentity(code);
            blockCertificate.setBlockTime(now);
            insertDOS.add(blockCertificate);
        });

        blockCertificateMapper.insertBatch(insertDOS);

        return insertDOS.stream().map(item -> {
            BlockCertificateRespVO respVO = new BlockCertificateRespVO();
            BeanUtil.copyProperties(item, respVO);
            return respVO;
        }).collect(Collectors.toList());
    }

    @Override
    public void updateStatusBatch(List<UpdateStatusReqVO> updateReqVOS) {

        Map<String, Integer> chainStatusMap = new HashMap<>();
        List<String> operateIds = updateReqVOS.stream().map(item -> {
            chainStatusMap.put(item.getChainOperateId(), item.getChainStatus());
            return item.getChainOperateId();
        }).collect(Collectors.toList());
        // 根据入参查找已有的区块证书
        List<BlockCertificateDO> blockCertificates = blockCertificateMapper.selectList(new LambdaQueryWrapperX<BlockCertificateDO>().in(BlockCertificateDO::getChainOperateId, operateIds));
        if(blockCertificates == null || blockCertificates.size() != updateReqVOS.size()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.BLOCK_CERTIFICATE_NOT_EXISTS);
        }

        blockCertificates.forEach(blockCertificate -> {
            if(blockCertificate.getChainState().equals(ChainStateEnum.NOT_ON_CHAIN.getState())) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.NOT_ON_CHAIN);
            }
            blockCertificate.setChainStatus(chainStatusMap.get(blockCertificate.getChainOperateId()).equals(ChainStatusEnum.ACTIVE.getStatus()) ? ChainStatusEnum.ACTIVE.getStatus() : ChainStatusEnum.FREEZE.getStatus());
        });

        blockCertificateMapper.updateBatch(blockCertificates, Integer.MAX_VALUE);
    }

    @Override
    public void upChain(Long id) {
        BlockCertificateDO blockCertificateDO = this.validateBlockCertificateExists(id);
        if(blockCertificateDO.getChainState().equals(ChainStateEnum.ON_CHAIN.getState())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.HAS_ON_CHAIN);
        }

        String code = StrUtils.getRandomUpperString(48).toLowerCase();
        Date now = new Date();
        blockCertificateDO.setChainBlockIdentity(code);
        blockCertificateDO.setBlockTime(now);
        blockCertificateDO.setChainState(ChainStateEnum.ON_CHAIN.getState());
        blockCertificateDO.setChainStatus(ChainStatusEnum.ACTIVE.getStatus());
        // 其他字段暂时不管

        blockCertificateMapper.updateById(blockCertificateDO);
    }

}
