package cn.iocoder.yudao.module.configure.service.tracetemplate;

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.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.configure.controller.admin.tracelink.vo.TraceLinkReqVO;
import cn.iocoder.yudao.module.configure.controller.admin.tracelink.vo.TraceLinkRespVO;
import cn.iocoder.yudao.module.configure.controller.admin.tracelink.vo.TraceLinkSaveReqVO;
import cn.iocoder.yudao.module.configure.controller.admin.tracelinkitem.vo.TraceLinkItemRespVO;
import cn.iocoder.yudao.module.configure.controller.admin.tracelinkitem.vo.TraceLinkItemSaveReqVO;
import cn.iocoder.yudao.module.configure.controller.admin.tracetemplate.vo.*;
import cn.iocoder.yudao.module.configure.convert.tracetemplate.TraceTemplateConvert;
import cn.iocoder.yudao.module.configure.dal.dataobject.tracelink.TraceLinkDO;
import cn.iocoder.yudao.module.configure.dal.dataobject.tracelinkitem.TraceLinkItemDO;
import cn.iocoder.yudao.module.configure.dal.dataobject.tracetemplate.TraceTemplateDO;
import cn.iocoder.yudao.module.configure.dal.mysql.tracelink.TraceLinkMapper;
import cn.iocoder.yudao.module.configure.dal.mysql.tracelinkitem.TraceLinkItemMapper;
import cn.iocoder.yudao.module.configure.dal.mysql.tracetemplate.TraceTemplateMapper;
import cn.iocoder.yudao.module.configure.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.configure.enums.LinkItemTypeEnum;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 溯源模板 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class TraceTemplateServiceImpl implements TraceTemplateService {

    @Resource
    private TraceTemplateMapper traceTemplateMapper;

    @Resource
    private TraceLinkMapper traceLinkMapper;

    @Resource
    private TraceLinkItemMapper traceLinkItemMapper;

    @Resource
    private BusinessInfoApi businessInfoApi;

    @Resource
    private GoodsInfoApi goodsInfoApi;

    @Override
    public Long createTraceTemplate(TraceTemplateCreateReqVO createReqVO) {
        TraceTemplateDO traceTemplate = new TraceTemplateDO();
        BeanUtil.copyProperties(createReqVO, traceTemplate);
        traceTemplate.setTemplateId(CodeEnum.TRACE_TEMPLATE_CODE.getCode());
        // 校验商户和商品
        this.verifyBusinessAndGoods(createReqVO.getBelongBusinessId(), createReqVO.getGoodsId(), traceTemplate);

        // 插入
        traceTemplateMapper.insert(traceTemplate);
        // 返回
        return traceTemplate.getId();
    }

    @Override
    public void updateTraceTemplate(TraceTemplateUpdateReqVO updateReqVO) {
        // 校验存在
        validateTraceTemplateExists(updateReqVO.getId());

        // 更新
        TraceTemplateDO updateObj = TraceTemplateConvert.INSTANCE.convert(updateReqVO);
        // 校验商户和商品
        this.verifyBusinessAndGoods(updateReqVO.getBelongBusinessId(), updateReqVO.getGoodsId(), updateObj);
        traceTemplateMapper.updateById(updateObj);
    }

    @Override
    @Transactional
    public void updateLinkConfig(LinkConfigUpdateReqVO updateReqVO) {
        // 校验存在
        TraceTemplateDO traceTemplateDO = this.validateTraceTemplateExists(updateReqVO.getId());

        // 查询数据库中已有的环节配置信息
        TraceLinkReqVO traceLinkReqVO = new TraceLinkReqVO();
        traceLinkReqVO.setTraceTemplateId(traceTemplateDO.getTemplateId());
        List<TraceLinkRespVO> traceLinksDB = traceLinkMapper.getTraceLinkList(traceLinkReqVO);

        Map<Long, TraceLinkRespVO> linksDBMap = new HashMap<>();
        Map<Long, TraceLinkItemRespVO> itemsDBMap = new HashMap<>();
        Map<Long, List<Long>> itemIdsDBMap = new HashMap<>();
        List<Long> linkIdsDB = traceLinksDB.stream().map(link -> {
            linksDBMap.put(link.getId(), link);
            link.getTraceLinkItems().forEach(item -> itemsDBMap.put(item.getId(), item));
            // 懒得另外定义一个变量， 这边再次循环一次
            itemIdsDBMap.put(link.getId(), link.getTraceLinkItems().stream().map(TraceLinkItemRespVO::getId).collect(Collectors.toList()));
            return link.getId();
        }).collect(Collectors.toList());

        List<TraceLinkDO> createTraceLinks = new ArrayList<>();// 将要创建的link集合
        List<TraceLinkItemDO> createLinkItems = new ArrayList<>();// 将要创建的linkItem集合
        List<TraceLinkDO> updateTraceLinks = new ArrayList<>();// 将要更新的link集合
        List<TraceLinkItemDO> updateLinkItems = new ArrayList<>();// 将要更新的linkItem集合
        List<Long> deleteLinkIds = new ArrayList<>();
        List<Long> deleteLinkItemIds = new ArrayList<>();

        // 循环
        List<TraceLinkSaveReqVO> LinksReq = updateReqVO.getLinks();

        if(LinksReq != null && !LinksReq.isEmpty()) {
            List<Long> willUpdateLinkIds = new ArrayList<>(); // 将要进行更新的环节id集合

            LinksReq.forEach(linkReq -> {
                // 判断id
                if(linkReq.getId() != null) {
                    // 编辑
                    if(!linkIdsDB.contains(linkReq.getId())) {
                        throw ServiceExceptionUtil.exception(ErrorCodeConstants.TRACE_LINK_NOT_EXISTS);
                    }
                    willUpdateLinkIds.add(linkReq.getId());

                    TraceLinkDO traceLinkDO = new TraceLinkDO();
                    BeanUtil.copyProperties(linksDBMap.get(linkReq.getId()), traceLinkDO);
                    traceLinkDO.setLinkName(linkReq.getLinkName());
                    traceLinkDO.setSort(linkReq.getSort());

                    List<Long> willUpdateItemIds = new ArrayList<>(); // 将要进行更新的环节item id集合
                    linkReq.getLinkItems().forEach(item -> {
                        if(item.getId() != null) {
                            if(!itemIdsDBMap.get(linkReq.getId()).contains(item.getId())) {
                                throw ServiceExceptionUtil.exception(ErrorCodeConstants.TRACE_LINK_ITEM_NOT_EXISTS);
                            }
                            willUpdateItemIds.add(item.getId());

                            TraceLinkItemDO traceLinkItemDO = new TraceLinkItemDO();
                            BeanUtil.copyProperties(itemsDBMap.get(item.getId()), traceLinkItemDO);
                            this.verifyFieldAndSetLinkItem(item, traceLinkItemDO);

                            updateLinkItems.add(traceLinkItemDO);
                        } else {
                            TraceLinkItemDO traceLinkItemDO = new TraceLinkItemDO();
                            this.setLinkItem(item, traceLinkItemDO, traceLinkDO);
                            createLinkItems.add(traceLinkItemDO);
                        }
                    });

                    deleteLinkItemIds.addAll(itemIdsDBMap.get(linkReq.getId()).stream().filter(id -> !willUpdateItemIds.contains(id)).collect(Collectors.toList()));
                    updateTraceLinks.add(traceLinkDO);
                } else {
                    // 新增
                    TraceLinkDO traceLinkDO = new TraceLinkDO();
                    this.setCreateLink(linkReq, traceLinkDO, traceTemplateDO, createLinkItems);
                    createTraceLinks.add(traceLinkDO);
                }
            });

            deleteLinkIds.addAll(linkIdsDB.stream().filter(id -> !willUpdateLinkIds.contains(id)).collect(Collectors.toList()));
        } else if(!linkIdsDB.isEmpty()) {
            // 将数据库里面的所有环节进行删除
            deleteLinkIds.addAll(linkIdsDB);
        }

        if(!createTraceLinks.isEmpty()) {
            traceLinkMapper.insertBatch(createTraceLinks);
        }

        if(!createLinkItems.isEmpty()) {
            traceLinkItemMapper.insertBatch(createLinkItems);
        }

        if(!updateTraceLinks.isEmpty()) {
            traceLinkMapper.updateBatch(updateTraceLinks, Integer.MAX_VALUE);
        }

        if(!updateLinkItems.isEmpty()) {
            traceLinkItemMapper.updateBatch(updateLinkItems, Integer.MAX_VALUE);
        }

        if(!deleteLinkIds.isEmpty()) {
            traceLinkMapper.deleteBatchIds(deleteLinkIds);
        }

        if(!deleteLinkItemIds.isEmpty()) {
            traceLinkItemMapper.deleteBatchIds(deleteLinkItemIds);
        }
    }

    private void verifyBusinessAndGoods(Long businessId, String goodsId, TraceTemplateDO traceTemplateDO) {
        BusinessInfoDTO businessInfo = businessInfoApi.getBusinessInfoById(businessId).getData();
        if(businessInfo == null) {
            throw ServiceExceptionUtil.exception(cn.iocoder.yudao.module.basic.enums.ErrorCodeConstants.BUSINESS_INFO_NOT_EXISTS);
        }
        List<String> goodsIds = new ArrayList<>();
        goodsIds.add(goodsId);
        List<GoodsInfoRespDTO> goodsInfos = goodsInfoApi.getGoodsInfoListByGoodsIds(goodsIds, businessInfo.getId()).getData();
        if(goodsInfos == null || goodsInfos.isEmpty() || goodsInfos.size() != goodsIds.size()) {
            throw ServiceExceptionUtil.exception(cn.iocoder.yudao.module.basic.enums.ErrorCodeConstants.GOODS_INFO_NOT_EXISTS);
        }
        if(!goodsInfos.get(0).getBelongBusinessId().equals(businessId)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.SELECT_GOODS_NOT_BELONG_TO_BUSINESS);
        }
        traceTemplateDO.setBelongBusinessName(businessInfo.getBusinessName());
        traceTemplateDO.setGoodsName(goodsInfos.get(0).getGoodsName());
    }

    private void setCreateLink(TraceLinkSaveReqVO reqVO, TraceLinkDO traceLinkDO, TraceTemplateDO traceTemplateDO, List<TraceLinkItemDO> traceLinkItems) {
        traceLinkDO.setId(null);
        traceLinkDO.setLinkId(CodeEnum.TRACE_LINK_CODE.getCode());
        traceLinkDO.setLinkName(reqVO.getLinkName());
        traceLinkDO.setTraceTemplateId(traceTemplateDO.getTemplateId());
        traceLinkDO.setTraceTemplateName(traceTemplateDO.getTemplateName());
        traceLinkDO.setBelongBusinessId(traceTemplateDO.getBelongBusinessId());
        traceLinkDO.setBelongBusinessName(traceTemplateDO.getBelongBusinessName());
        traceLinkDO.setSort(reqVO.getSort());
        reqVO.getLinkItems().forEach(item -> {
            TraceLinkItemDO traceLinkItemDO = new TraceLinkItemDO();
            this.setLinkItem(item, traceLinkItemDO, traceLinkDO);
            traceLinkItemDO.setId(null);
            traceLinkItems.add(traceLinkItemDO);
        });
    }

    private void setLinkItem(TraceLinkItemSaveReqVO item, TraceLinkItemDO traceLinkItemDO, TraceLinkDO traceLinkDO) {
        BeanUtil.copyProperties(item, traceLinkItemDO);
        traceLinkItemDO.setLinkId(traceLinkDO.getLinkId());
        traceLinkItemDO.setLinkName(traceLinkDO.getLinkName());
        traceLinkItemDO.setBelongBusinessId(traceLinkDO.getBelongBusinessId());
        traceLinkItemDO.setBelongBusinessName(traceLinkDO.getBelongBusinessName());

        this.verifyFieldAndSetLinkItem(item, traceLinkItemDO);
    }

    private void verifyFieldAndSetLinkItem(TraceLinkItemSaveReqVO item, TraceLinkItemDO traceLinkItemDO) {
        traceLinkItemDO.setTitle(item.getTitle());
        traceLinkItemDO.setContent(item.getContent());
        if(!item.getType().equals(LinkItemTypeEnum.TEXT.getType()) && !item.getType().equals(LinkItemTypeEnum.NO_TITLE_TEXT.getType())) {
            traceLinkItemDO.setLinkText("");
        } else {
            traceLinkItemDO.setLinkText(item.getLinkText());
        }
        if(item.getTitle() == null) {
            traceLinkItemDO.setTitle("");// 设置一个空字符串
        }
        if(item.getContent() == null) {
            traceLinkItemDO.setContent("");// 设置一个空字符串
        }
    }

    @Override
    public void deleteTraceTemplate(Long id) {
        // 校验存在
        validateTraceTemplateExists(id);
        // 删除
        traceTemplateMapper.deleteById(id);
    }

    private TraceTemplateDO validateTraceTemplateExists(Long id) {
        TraceTemplateDO traceTemplateDO = traceTemplateMapper.selectById(id);
        if (traceTemplateDO == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.TRACE_TEMPLATE_NOT_EXISTS);
        }
        return traceTemplateDO;
    }

    @Override
    public TraceTemplateDO getTraceTemplate(Long id) {
        return traceTemplateMapper.selectById(id);
    }

    @Override
    public PageResult<TraceTemplateRespVO> getTraceTemplatePage(TraceTemplatePageReqVO pageReqVO) {
        Page<TraceTemplateRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<TraceTemplateRespVO> result = traceTemplateMapper.selectTraceTemplatePage(page, pageReqVO);
        return new PageResult<>(result, page.getTotal());
    }

    @Override
    public List<TraceTemplateRespVO> getTraceTemplateListByGoodsId(String goodsId) {
        TraceTempReqVO reqVO = new TraceTempReqVO();
        reqVO.setGoodsId(goodsId);
        return traceTemplateMapper.getTraceTemplateList(reqVO);
    }

    @Override
    public List<TraceLinkRespVO> getLinkListByTemplateId(String templateId) {
        TraceLinkReqVO reqVO = new TraceLinkReqVO();
        reqVO.setTraceTemplateId(templateId);
        return traceLinkMapper.getTraceLinkList(reqVO);
    }

    @Override
    @Transactional
    public Integer copyLinkConfigToTemplate(TraceTemplateCopyReqVO copyReqVO) {
        TraceTemplateDO createTraceTemplate = new TraceTemplateDO();
        BeanUtil.copyProperties(copyReqVO, createTraceTemplate);
        // 校验商户和商品
        this.verifyBusinessAndGoods(copyReqVO.getBelongBusinessId(), copyReqVO.getGoodsId(), createTraceTemplate);
        createTraceTemplate.setTemplateId(CodeEnum.TRACE_TEMPLATE_CODE.getCode());

        List<TraceLinkDO> traceLinks = new ArrayList<>();
        List<TraceLinkItemDO> traceLinkItems = new ArrayList<>();

        copyReqVO.getLinks().forEach(link -> {
            TraceLinkDO traceLinkDO = new TraceLinkDO();
            this.setCreateLink(link, traceLinkDO, createTraceTemplate, traceLinkItems);
            traceLinks.add(traceLinkDO);
        });

        // 必定不会为空， 所以这里不做多余判断
        traceLinkMapper.insertBatch(traceLinks);

        traceLinkItemMapper.insertBatch(traceLinkItems);

        return traceTemplateMapper.insert(createTraceTemplate);
    }

}
