package cn.iocoder.yudao.module.product.service.harvestinfo;

import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.framework.common.enums.BlockStateEnum;
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.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.basic.api.businessinfo.BusinessInfoApi;
import cn.iocoder.yudao.module.basic.api.goodsinfo.GoodsInfoApi;
import cn.iocoder.yudao.module.basic.api.goodsinfo.dto.GoodsInfoRespDTO;
import cn.iocoder.yudao.module.basic.api.redis.dto.BusinessInfoDTO;
import cn.iocoder.yudao.module.chain.api.blockcertificate.BlockCertificateApi;
import cn.iocoder.yudao.module.chain.api.blockcertificate.dto.BlockCertificateRespDTO;
import cn.iocoder.yudao.module.chain.api.blockcertificate.dto.BlockCertificateUpChainReqDTO;
import cn.iocoder.yudao.module.chain.api.blockcertificate.dto.UpChainObjReqDTO;
import cn.iocoder.yudao.module.chain.api.blockcertificate.dto.UpdateStatusReqDTO;
import cn.iocoder.yudao.module.chain.enums.BatchTypeEnum;
import cn.iocoder.yudao.module.chain.enums.ChainStatusEnum;
import cn.iocoder.yudao.module.chain.enums.OperateTypeEnum;
import cn.iocoder.yudao.module.configure.api.uniTemplate.UniTemplateApi;
import cn.iocoder.yudao.module.configure.api.uniTemplate.dto.UniTemplateDTO;
import cn.iocoder.yudao.module.product.controller.admin.harvestbatch.vo.HarvestBatchBaseVO;
import cn.iocoder.yudao.module.product.controller.admin.harvestbatch.vo.HarvestBatchRespVO;
import cn.iocoder.yudao.module.product.controller.admin.harvestbatch.vo.HarvestBatchSaveReqVO;
import cn.iocoder.yudao.module.product.controller.admin.harvestinfo.vo.*;
import cn.iocoder.yudao.module.product.convert.harvestbatch.HarvestBatchConvert;
import cn.iocoder.yudao.module.product.convert.harvestinfo.HarvestInfoConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.harvestbatch.HarvestBatchDO;
import cn.iocoder.yudao.module.product.dal.dataobject.harvestbatchconfig.HarvestBatchConfigDO;
import cn.iocoder.yudao.module.product.dal.dataobject.harvestinfo.HarvestInfoDO;
import cn.iocoder.yudao.module.product.dal.mysql.harvestbatch.HarvestBatchMapper;
import cn.iocoder.yudao.module.product.dal.mysql.harvestbatchconfig.HarvestBatchConfigMapper;
import cn.iocoder.yudao.module.product.dal.mysql.harvestinfo.HarvestInfoMapper;
import cn.iocoder.yudao.module.product.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.system.api.languagetype.LanguageTypeApi;
import cn.iocoder.yudao.module.system.api.languagetype.dto.LanguageTypeRespDTO;
import io.seata.spring.annotation.GlobalTransactional;
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 HarvestInfoServiceImpl implements HarvestInfoService {

    @Resource
    private HarvestInfoMapper harvestInfoMapper;

    @Resource
    private HarvestBatchMapper harvestBatchMapper;

    @Resource
    private HarvestBatchConfigMapper harvestBatchConfigMapper;

    @Resource
    private GoodsInfoApi goodsInfoApi;

    @Resource
    private BusinessInfoApi businessInfoApi;

    @Resource
    private BlockCertificateApi blockCertificateApi;

    @Resource
    private UniTemplateApi uniTemplateApi;

    @Resource
    private LanguageTypeApi languageTypeApi;

    @Override
    @Transactional
    public Long createHarvestInfo(HarvestInfoCreateReqVO createReqVO) {
        // 校验商户是否存在
        BusinessInfoDTO businessInfo = businessInfoApi.getBusinessInfoById(createReqVO.getBelongBusinessId()).getData();
        if(businessInfo == null) {
            throw ServiceExceptionUtil.exception(cn.iocoder.yudao.module.basic.enums.ErrorCodeConstants.BUSINESS_INFO_NOT_EXISTS);
        }
        // 获取默认模板
        UniTemplateDTO templateDTO = uniTemplateApi.getDefaultTemplate().getData();
        if (templateDTO == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CAN_NOT_FIND_DEFAULT_UNI_TEMPLATE);
        }

        // 查询所有的语言类型
        List<LanguageTypeRespDTO> languageTypeList = languageTypeApi.getAllLanguageType().getData();

        Map<String, GoodsInfoRespDTO> goodsInfoMap = new HashMap<>();
        List<HarvestBatchConfigDO> createBatchConfigList = new ArrayList<>();

        // 校验商品信息
        this.verifiGoodsInfos(createReqVO.getHarvestBatchs(), goodsInfoMap, null, createReqVO.getBelongBusinessId());

        List<HarvestBatchDO> HarvestBatchDOs = HarvestBatchConvert.INSTANCE.convertListForCreate(createReqVO.getHarvestBatchs());

        HarvestBatchDOs.forEach(harvestBatch -> {
            // 封装参数
            harvestBatch.setId(null);
            harvestBatch.setHarvestId(createReqVO.getHarverstId());
            harvestBatch.setBlockState(BlockStateEnum.NOT_ON_CHAIN.getBlockState());
            harvestBatch.setBelongBusinessId(goodsInfoMap.get(harvestBatch.getGoodsId()).getBelongBusinessId());
            harvestBatch.setBelongBusinessName(goodsInfoMap.get(harvestBatch.getGoodsId()).getBelongBusinessName());
            // 商品名以数据库的为准
            harvestBatch.setGoodsName(goodsInfoMap.get(harvestBatch.getGoodsId()).getGoodsName());

            languageTypeList.forEach(languageType -> {
                HarvestBatchConfigDO config = new HarvestBatchConfigDO();
                config.setLanguageTypeId(languageType.getId());
                config.setHarvestBatchId(harvestBatch.getHarvestBatchId());
                config.setTemplateId(templateDTO.getId());
                createBatchConfigList.add(config);
            });
        });

        HarvestInfoDO harvestInfo = HarvestInfoConvert.INSTANCE.convert(createReqVO);

        // 设置上链状态
        harvestInfo.setBlockState(BlockStateEnum.NOT_ON_CHAIN.getBlockState());
        harvestInfo.setBelongBusinessName(businessInfo.getBusinessName());

        harvestInfoMapper.insert(harvestInfo);

        harvestBatchMapper.insertBatch(HarvestBatchDOs);

        if (!createBatchConfigList.isEmpty()) {
            harvestBatchConfigMapper.insertBatch(createBatchConfigList);
        }
        // 返回
        return harvestInfo.getId();
    }

    @Override
    @Transactional
    public void updateHarvestInfo(HarvestInfoUpdateReqVO updateReqVO) {
        // 校验存在
        HarvestInfoDO harvestInfo = harvestInfoMapper.selectById(updateReqVO.getId());
        if (harvestInfo == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.HARVEST_INFO_NOT_EXISTS);
        }
        if(!harvestInfo.getBlockState().equals(BlockStateEnum.NOT_ON_CHAIN.getBlockState())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.ONLY_NOT_ON_CHAIN_HARVEST_INFO_CAN_BE_EDIT);
        }

        Map<String, GoodsInfoRespDTO> goodsInfoMap = new HashMap<>();
        List<Long> willUpdateIds = new ArrayList<>();// 将要更新的投入品批次主键id
        this.verifiGoodsInfos(updateReqVO.getHarvestBatchs(), goodsInfoMap, willUpdateIds, harvestInfo.getBelongBusinessId());

        // 验证将要更新的批次
        Map<Long, HarvestBatchDO> harvestBatchMaps = new HashMap<>();// 采收批次键值对
        // 验证将要更新的批次
        if(willUpdateIds.size() > 0) {
            List<HarvestBatchDO> harvestBatchDOs = harvestBatchMapper.selectList(new LambdaQueryWrapperX<HarvestBatchDO>()
                .in(HarvestBatchDO::getId, willUpdateIds)
                .eq(HarvestBatchDO::getHarvestId, harvestInfo.getHarverstId()));
            if(harvestBatchDOs.size() != willUpdateIds.size()) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.HARVEST_BATCH_NOT_EXISTS);
            }

            harvestBatchDOs.forEach(item -> harvestBatchMaps.put(item.getId(), item));
        }

        List<HarvestBatchDO> addList = new ArrayList<>(); // 需要新增的批次集合
        List<HarvestBatchDO> updateList = new ArrayList<>(); // 需要新增的批次集合
        for(HarvestBatchSaveReqVO item: updateReqVO.getHarvestBatchs()) {
            if(item.getId() != null) {
                // 更新流程
                HarvestBatchDO harvestBatchDO = harvestBatchMaps.get(item.getId());
                // 只更新可更新的字段
                harvestBatchDO.setGoodsNum(item.getGoodsNum());
                harvestBatchDO.setHarvestDate(item.getHarvestDate());
                harvestBatchDO.setRemarks(item.getRemarks());

                updateList.add(harvestBatchDO);
            } else {
                // 新增流程
                HarvestBatchDO harvestBatchDO = new HarvestBatchDO();
                BeanUtil.copyProperties(item, harvestBatchDO);
                harvestBatchDO.setHarvestId(harvestInfo.getHarverstId());
                harvestBatchDO.setBlockState(BlockStateEnum.NOT_ON_CHAIN.getBlockState());
                harvestBatchDO.setGoodsName(goodsInfoMap.get(item.getGoodsId()).getGoodsName());
                harvestBatchDO.setBelongBusinessId(goodsInfoMap.get(item.getGoodsId()).getBelongBusinessId());
                harvestBatchDO.setBelongBusinessName(goodsInfoMap.get(item.getGoodsId()).getBelongBusinessName());

                addList.add(harvestBatchDO);
            }
        }

        // 封装主单更新的参数
        HarvestInfoDO harvestInfoDO = new HarvestInfoDO();
        BeanUtil.copyProperties(updateReqVO, harvestInfoDO);

        harvestInfoMapper.updateById(harvestInfoDO);

        if(updateList.size() > 0) {
            harvestBatchMapper.updateBatch(updateList, Integer.MAX_VALUE);
        }

        if(addList.size() > 0) {
            harvestBatchMapper.insertBatch(addList);
        }
    }

    private void verifiGoodsInfos(List<HarvestBatchSaveReqVO> harvestBatchs, Map<String, GoodsInfoRespDTO> goodsInfoMap, List<Long> willUpdateIds, Long businessInfoId) {
        Set<String> goodsInfoIdsSet = new HashSet<>();
        Set<Long> willUpdateIdsSet = new HashSet<>();

        for(HarvestBatchSaveReqVO batchs: harvestBatchs) {
            goodsInfoIdsSet.add(batchs.getGoodsId());
            if(batchs.getId() != null) {
                willUpdateIdsSet.add(batchs.getId());
                if(willUpdateIds != null) {
                    willUpdateIds.add(batchs.getId());
                }
            }
        }

        // 存在相同的商品编号
        if(goodsInfoIdsSet.size() != harvestBatchs.size()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CAN_NOT_ADD_THE_SAME_GOODS_INFO);
        }
        // 存在相同的批次编号
        if(willUpdateIds != null && willUpdateIdsSet.size() != willUpdateIds.size()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.EXIST_THE_SAME_HARVEST_BATCH);
        }

        // 判断商品是否存在
        List<String> goodsIds = new ArrayList<>(goodsInfoIdsSet);
        List<GoodsInfoRespDTO> goodsInfos = goodsInfoApi.getGoodsInfoListByGoodsIds(goodsIds, businessInfoId).getData();
        if(goodsInfos == null || goodsInfos.isEmpty() || goodsInfos.size() != goodsIds.size()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.GOODS_INFO_NOT_EXISTS);
        }

        for(GoodsInfoRespDTO goods: goodsInfos) {
            goodsInfoMap.put(goods.getGoodsId(), goods);
        }
    }

    @Override
    public void deleteHarvestInfo(Long id) {
        // 校验存在
        validateHarvestInfoExists(id);
        // 删除
        harvestInfoMapper.deleteById(id);
    }

    private void validateHarvestInfoExists(Long id) {
        if (harvestInfoMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.HARVEST_INFO_NOT_EXISTS);
        }
    }

    @Override
    public HarvestInfoRespVO getHarvestInfo(Long id) {
        HarvestInfoDO harvestInfo = harvestInfoMapper.selectById(id);
        if(harvestInfo == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.HARVEST_INFO_NOT_EXISTS);
        }

        HarvestInfoRespVO harvestInfoRespVO = HarvestInfoConvert.INSTANCE.convert(harvestInfo);
        List<HarvestBatchDO> list = harvestBatchMapper.selectList(HarvestBatchDO::getHarvestId, harvestInfo.getHarverstId());
        List<HarvestBatchRespVO> harvestBatchResps = HarvestBatchConvert.INSTANCE.convertList(list);
        List<String> goodsIds = harvestBatchResps.stream().map(HarvestBatchBaseVO::getGoodsId).collect(Collectors.toList());
        List<GoodsInfoRespDTO> goodsInfos = goodsInfoApi.getGoodsInfoListByGoodsIds(goodsIds, harvestInfo.getBelongBusinessId()).getData();
        if(goodsInfos != null && !goodsInfos.isEmpty() || Objects.requireNonNull(goodsInfos).size() != goodsIds.size()) {
            Map<String, GoodsInfoRespDTO> goodsInfoMap = new HashMap<>();

            goodsInfos.forEach(item -> goodsInfoMap.put(item.getGoodsId(), item));
            for(HarvestBatchRespVO respVo: harvestBatchResps) {
                respVo.setGoodsUnit(goodsInfoMap.get(respVo.getGoodsId()).getGoodsUnit());
                respVo.setGoodsSpecs(goodsInfoMap.get(respVo.getGoodsId()).getGoodsSpecs());
            }
        }

        harvestInfoRespVO.setHarvestBatchs(harvestBatchResps);
        return harvestInfoRespVO;
    }

    @Override
    public List<HarvestInfoDO> getHarvestInfoList(Collection<Long> ids) {
        return harvestInfoMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<HarvestInfoDO> getHarvestInfoPage(HarvestInfoPageReqVO pageReqVO) {
        return harvestInfoMapper.selectPage(pageReqVO);
    }

    @Override
    public List<HarvestInfoDO> getHarvestInfoList(HarvestInfoExportReqVO exportReqVO) {
        return harvestInfoMapper.selectList(exportReqVO);
    }

    @Override
    public List<String> getHarverstIdList(Integer count) {
        List<String> results = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            results.add(CodeEnum.HARVEST_INFO_CODE.getCode());
        }
        return results;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void upChain(Long id) {
        HarvestInfoDO harvestInfo = harvestInfoMapper.selectById(id);
        if(harvestInfo == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.HARVEST_INFO_NOT_EXISTS);
        }
        if(!harvestInfo.getBlockState().equals(BlockStateEnum.NOT_ON_CHAIN.getBlockState())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.ONLY_NOT_ON_CHAIN_HARVEST_INFO_CAN_DO_UP_CHAIN);
        }
        // 查询所有批次
        List<HarvestBatchDO> harvestBatchs = harvestBatchMapper.selectList(new LambdaQueryWrapperX<HarvestBatchDO>().eq(HarvestBatchDO::getHarvestId, harvestInfo.getHarverstId()));

        // 封装批次
        BlockCertificateUpChainReqDTO upChainBatchReqDTO = new BlockCertificateUpChainReqDTO();
        List<UpChainObjReqDTO> operateBatchs = harvestBatchs.stream().map(batch -> {
            UpChainObjReqDTO upChainObjReqDTO = new UpChainObjReqDTO();
            upChainObjReqDTO.setBatchId(batch.getHarvestBatchId());
            upChainObjReqDTO.setOperateObjId(batch.getId());
            return upChainObjReqDTO;
        }).collect(Collectors.toList());
        upChainBatchReqDTO.setOperateType(OperateTypeEnum.BATCH.getType());
        upChainBatchReqDTO.setOperateName(OperateTypeEnum.BATCH.getName());
        upChainBatchReqDTO.setBatchType(BatchTypeEnum.HARVEST_BATCH.getType());
        upChainBatchReqDTO.setOperateObjs(operateBatchs);
        upChainBatchReqDTO.setBelongBusinessId(harvestInfo.getBelongBusinessId());
        upChainBatchReqDTO.setBelongBusinessName(harvestInfo.getBelongBusinessName());

        // 封装入库
        List<UpChainObjReqDTO> operateHarvests = new ArrayList<>();
        UpChainObjReqDTO upChainObjReqDTO = new UpChainObjReqDTO();
        upChainObjReqDTO.setOperateObjId(id);
        operateHarvests.add(upChainObjReqDTO);

        BlockCertificateUpChainReqDTO upChainReqDTO = new BlockCertificateUpChainReqDTO();
        upChainReqDTO.setOperateType(OperateTypeEnum.PUT_IN.getType());
        upChainReqDTO.setOperateName(OperateTypeEnum.PUT_IN.getName());
        upChainReqDTO.setOperateObjs(operateHarvests);
        upChainReqDTO.setBelongBusinessId(harvestInfo.getBelongBusinessId());
        upChainReqDTO.setBelongBusinessName(harvestInfo.getBelongBusinessName());

        // 远程调用
        List<BlockCertificateRespDTO> harvestResults = blockCertificateApi.createAndUpChainBatch(upChainReqDTO).getData();
        List<BlockCertificateRespDTO> batchResults = blockCertificateApi.createAndUpChainBatch(upChainBatchReqDTO).getData();
        if(batchResults == null || harvestResults == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CREATE_BLOCK_CERTIFICATE_FAIL);
        }
        Map<String, BlockCertificateRespDTO> batchBCMaps = new HashMap<>();
        batchResults.forEach(item -> batchBCMaps.put(item.getBatchId(), item));

        // 将要更新的类
        HarvestInfoDO harvestInfoDO = new HarvestInfoDO();
        harvestInfoDO.setId(id);
        harvestInfoDO.setBlockChainBlockIdentity(harvestResults.get(0).getChainBlockIdentity());
        harvestInfoDO.setBlockChainId(harvestResults.get(0).getChainOperateId());
        harvestInfoDO.setBlockState(BlockStateEnum.ACTIVATION.getBlockState());// 改为激活状态

        List<HarvestBatchDO> harvestBatchDOS = new ArrayList<>();
        //循环所有批次
        harvestBatchs.forEach(item -> {
            HarvestBatchDO harvestBatchDO = new HarvestBatchDO();
            BeanUtil.copyProperties(item, harvestBatchDO);
            harvestBatchDO.setBlockChainBlockIdentity(batchBCMaps.get(item.getHarvestBatchId()).getChainBlockIdentity());
            harvestBatchDO.setBlockChainId(batchBCMaps.get(item.getHarvestBatchId()).getChainOperateId());
            harvestBatchDO.setBlockState(BlockStateEnum.ACTIVATION.getBlockState());// 修改主单的激活状态时， 子单一律改为激活
            harvestBatchDOS.add(harvestBatchDO);
        });

        harvestInfoMapper.updateById(harvestInfoDO);
        harvestBatchMapper.updateBatch(harvestBatchDOS, Integer.MAX_VALUE);
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void updateChainStatus(Long id, Integer state) {
        HarvestInfoDO harvestInfo = harvestInfoMapper.selectById(id);
        if(harvestInfo == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.HARVEST_INFO_NOT_EXISTS);
        }
        if(harvestInfo.getBlockState().equals(BlockStateEnum.NOT_ON_CHAIN.getBlockState())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.ONLY_ON_CHAIN_HARVEST_INFO_CAN_DO_UPDATE_CHAIN_STATUS);
        }
        // 查询所有批次
        List<HarvestBatchDO> harvestBatchs = harvestBatchMapper.selectList(new LambdaQueryWrapperX<HarvestBatchDO>().eq(HarvestBatchDO::getHarvestId, harvestInfo.getHarverstId()));

        Integer blockState = state.equals(BlockStateEnum.ACTIVATION.getBlockState()) ? BlockStateEnum.ACTIVATION.getBlockState() : BlockStateEnum.FREEZE.getBlockState();
        Integer chainStatus = state.equals(BlockStateEnum.ACTIVATION.getBlockState()) ? ChainStatusEnum.ACTIVE.getStatus() : ChainStatusEnum.FREEZE.getStatus();
        // 修改主单的链状态时，将同步修改子单的链状态
        List<UpdateStatusReqDTO> batchsReqDTOS = new ArrayList<>();
        UpdateStatusReqDTO harvestReqDTO = new UpdateStatusReqDTO();
        harvestReqDTO.setChainOperateId(harvestInfo.getBlockChainId());
        harvestReqDTO.setChainStatus(chainStatus);
        batchsReqDTOS.add(harvestReqDTO);
        harvestBatchs.forEach(batch -> {
            UpdateStatusReqDTO batchReqDTO = new UpdateStatusReqDTO();
            batchReqDTO.setChainOperateId(batch.getBlockChainId());
            batchReqDTO.setChainStatus(chainStatus);
            batchsReqDTOS.add(batchReqDTO);
        });

        if(blockCertificateApi.updateStatusBatch(batchsReqDTOS).isError()) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.UPDATE_CREATE_BLOCK_CERTIFICATE_FAIL);
        }

        HarvestInfoDO updateDO = new HarvestInfoDO();
        updateDO.setId(id);
        updateDO.setBlockState(blockState);

        harvestBatchs.forEach(batch -> batch.setBlockState(blockState));

        harvestInfoMapper.updateById(updateDO);
        harvestBatchMapper.updateBatch(harvestBatchs, Integer.MAX_VALUE);
    }

}
