package com.dayou.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.dayou.brokerage.annotation.DoBrokerage;
import com.dayou.brokerage.constants.BrokerageRule;
import com.dayou.entity.BrokerageVariable;
import com.dayou.entity.Item;
import com.dayou.entity.ItemStage;
import com.dayou.entity.ItemStageUser;
import com.dayou.enums.ItemStateEnum;
import com.dayou.exception.ErrorCode;
import com.dayou.mapper.ItemMapper;
import com.dayou.mapper.ItemStageMapper;
import com.dayou.service.IItemStageService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dayou.service.IItemStageUserService;
import com.dayou.vo.ItemStageUserVO;
import com.dayou.vo.ItemStageVO;
import com.dayou.vo.ItemVO;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;


import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.transaction.annotation.Transactional;
import com.dayou.enums.BatchTaskTypeEnum;

import static com.dayou.enums.OperationTypeEnum.*;
import static com.dayou.exception.ErrorCode.*;

/**
 * <p>
 * 项目阶段表 服务实现类
 * </p>
 *
 * @author wucl
 * @since 2022-12-08
 */
@Service
public class ItemStageServiceImpl extends ServiceImpl<ItemStageMapper, ItemStage> implements IItemStageService {


    @Autowired
    private ItemStageMapper itemStageMapper;

    @Autowired
    private ItemMapper itemMapper;

    @Autowired
    private IItemStageUserService iItemStageUserService;


    @Override
    @SuppressWarnings("unchecked")
    public Page<ItemStage> selectPage(Page page,ItemStage itemStage){
        return this.page(page, new QueryWrapper<ItemStage>(itemStage));
    }


    @Override
    public ItemStage detail(Long id){
        return this.getById(id);
    }

    @Transactional
    @Override
    @DoBrokerage(operation = ADD,rule = BrokerageRule.LAND_OTHER_RULE)
    public Long add(ItemStageVO itemStageVO){
        //判断阶段时间是否与已有阶段时间重复
        LocalDate startDate = itemStageVO.getStartDate();
        LocalDate endDate = itemStageVO.getEndDate();
        int check = itemStageMapper.checkDate(itemStageVO.getId(),itemStageVO.getItemId(),startDate,endDate);
        if (check!=0){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"日期段与已存在的排期日期重复,请检查.");
        }
        if (StrUtil.isNotBlank(itemStageVO.getHeaderId())){
            itemStageVO.setFooterId(UUID.randomUUID().toString());
            this.update(new LambdaUpdateWrapper<ItemStage>()
                    .set(ItemStage::getHeaderId,itemStageVO.getFooterId())
                    .eq(ItemStage::getHeaderId,itemStageVO.getHeaderId()));
        }else {
            int count = this.count(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getItemId, itemStageVO.getItemId()));
            if (count>0){
                ErrorCode.throwBusinessException(STAGE_NODE_ERROR);
            }
            itemStageVO.setFooterId(UUID.randomUUID().toString());
        }

        if (itemStageVO.getWeight().compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN))== 1){
            ErrorCode.throwBusinessException(STAGE_NODE_ERROR2);
        }
        List<BigDecimal> sumWeight = this.list(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getItemId, itemStageVO.getItemId())).stream().map(ItemStage::getWeight).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(sumWeight)){
            BigDecimal sum = sumWeight.stream().reduce(BigDecimal.ZERO, BigDecimal::add).add(itemStageVO.getWeight());
            if (sum.compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN)) == 1){
                ErrorCode.throwBusinessException(STAGE_NODE_ERROR2);
            }
        }
        this.save(itemStageVO);

        Set<ItemStageVO.ParticipatorWeight> partWeights = itemStageVO.getParticipators().stream().filter(s -> s.getWeight() != null).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(partWeights)){
            BigDecimal reduce = partWeights.stream().map(ItemStageVO.ParticipatorWeight::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (reduce.compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN)) == 1){
                ErrorCode.throwBusinessException(STAGE_NODE_ERROR3);
            }
        }

        List<ItemStageUser> list = iItemStageUserService.list(new LambdaQueryWrapper<ItemStageUser>().eq(ItemStageUser::getItemId, itemStageVO.getItemId()).eq(ItemStageUser::getStageId, itemStageVO.getId()));
        if (CollectionUtils.isNotEmpty(list)){
            BigDecimal reduce = list.stream().map(ItemStageUser::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add).add(partWeights.stream().map(ItemStageVO.ParticipatorWeight::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add));
            if (reduce.compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN)) == 1){
                ErrorCode.throwBusinessException(STAGE_NODE_ERROR3);
            }
        }
        Set<ItemStageUser> itemStageUsers = partWeights.stream().map(x -> {
            return ItemStageUser.builder().itemId(itemStageVO.getItemId()).stageId(itemStageVO.getId()).userId(x.getUserId()).weight(x.getWeight()).build();
        }).collect(Collectors.toSet());
        iItemStageUserService.saveBatch(itemStageUsers);
        return itemStageVO.getId();
    }

    @Transactional
    @Override
    @DoBrokerage(operation = EDIT,rule = BrokerageRule.LAND_OTHER_RULE)
    public Boolean update(ItemStageVO itemStageVO){
        //判断阶段时间是否与已有阶段时间重复
        LocalDate startDate = itemStageVO.getStartDate();
        LocalDate endDate = itemStageVO.getEndDate();
        int check = itemStageMapper.checkDate(itemStageVO.getId(),itemStageVO.getItemId(),startDate,endDate);
        if (check!=0){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"日期段与已存在的排期日期重复,请检查.");
        }
        //原节点headerId
        ItemStage oldStage = this.getById(itemStageVO.getId());
        if (StrUtil.isBlank(itemStageVO.getHeaderId()) && StrUtil.isNotBlank(oldStage.getHeaderId())){
            ErrorCode.throwBusinessException(STAGE_NODE_ERROR);
        }
        if (StrUtil.isNotBlank(itemStageVO.getHeaderId()) &&
                StrUtil.isNotBlank(itemStageVO.getFooterId()) &&
                StrUtil.equals(itemStageVO.getHeaderId(),itemStageVO.getFooterId())){
            ErrorCode.throwBusinessException(STAGE_NODE_ERROR1);
        }

        if (itemStageVO.getWeight().compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN))== 1){
            ErrorCode.throwBusinessException(STAGE_NODE_ERROR2);
        }
        List<BigDecimal> sumWeight = this.list(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getItemId, itemStageVO.getItemId())).stream().map(ItemStage::getWeight).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(sumWeight)){
            BigDecimal sum = sumWeight.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
            if (sum.compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN)) == 1){
                ErrorCode.throwBusinessException(STAGE_NODE_ERROR2);
            }
        }

        Set<ItemStageVO.ParticipatorWeight> partWeights = itemStageVO.getParticipators().stream().filter(s -> s.getWeight() != null).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(partWeights)){
            BigDecimal reduce = partWeights.stream().map(ItemStageVO.ParticipatorWeight::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (reduce.compareTo(BigDecimal.TEN.multiply(BigDecimal.TEN)) == 1){
                ErrorCode.throwBusinessException(STAGE_NODE_ERROR3);
            }
        }

        ItemStage stage1 = this.getOne(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getHeaderId, itemStageVO.getHeaderId()));
        ItemStage stage2 = this.getOne(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getHeaderId, oldStage.getFooterId()));
        if (stage1!=null && stage1.getId() != itemStageVO.getId()){
            stage1.setHeaderId(oldStage.getFooterId());
            this.update(new LambdaUpdateWrapper<ItemStage>().set(ItemStage::getHeaderId,oldStage.getFooterId()).eq(ItemStage::getId,stage1.getId()));
        }
        if (stage2!=null){
            this.update(new LambdaUpdateWrapper<ItemStage>().set(ItemStage::getHeaderId,oldStage.getFooterId()).eq(ItemStage::getId,stage2.getId()));
        }
        this.updateById(itemStageVO);

        iItemStageUserService.remove(new LambdaQueryWrapper<ItemStageUser>().eq(ItemStageUser::getStageId,itemStageVO.getId()));
        Set<ItemStageVO.ParticipatorWeight> participators = itemStageVO.getParticipators();
        if (CollectionUtils.isNotEmpty(participators)){
            Set<ItemStageUser> itemStageUsers = participators.stream().filter(s->s.getWeight()!=null).map(x -> {
                return ItemStageUser.builder().itemId(itemStageVO.getItemId()).stageId(itemStageVO.getId()).userId(x.getUserId()).weight(x.getWeight()).build();
            }).collect(Collectors.toSet());
            return iItemStageUserService.saveBatch(itemStageUsers);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional
    @DoBrokerage(operation = DELETE,rule = BrokerageRule.LAND_OTHER_RULE)
    public Boolean delete(Long id){
        ItemStage itemStage = this.getById(id);
        if (itemStage.getHeaderId()==null){
            ItemStage secondStage = this.getOne(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getHeaderId, itemStage.getFooterId()));
            if (secondStage!=null){
                this.update(new LambdaUpdateWrapper<ItemStage>().set(ItemStage::getHeaderId,null).eq(ItemStage::getId,secondStage.getId()));
            }
        }else {
            ItemStage footerStage = this.getOne(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getHeaderId, itemStage.getFooterId()));
            ItemStage headerStage = this.getOne(new LambdaQueryWrapper<ItemStage>().eq(ItemStage::getFooterId, itemStage.getHeaderId()));
            if (footerStage!=null && headerStage!=null){
                this.update(new LambdaUpdateWrapper<ItemStage>().set(ItemStage::getHeaderId,headerStage.getFooterId()).eq(ItemStage::getId,footerStage.getId()));
            }
        }
        this.removeById(id);
        return iItemStageUserService.remove(new LambdaQueryWrapper<ItemStageUser>().eq(ItemStageUser::getStageId,id));
    }

    @Override
    public List<ItemStageVO> listByItemId(Long itemId) {
        List<ItemStageVO> stages = itemStageMapper.listByItemId(itemId);
        stages.stream().forEach(x->{
            Set<ItemStageUserVO> list = iItemStageUserService.getList(x.getId());
            x.setParticipated(list);
        });
        return toSort(stages,new LinkedList<>(),null);
    }

    @Override
    public List<List<ItemStageVO>> stageList(ItemStageVO stageVO) {
        String state = stageVO.getState();
        List<List<ItemStageVO>> list = new ArrayList<>();
        List<ItemVO> items = itemMapper.xSelectList(stageVO);

        items.stream().forEach(x->{
            List<ItemStageVO> itemStageVOS = listByItemId(x.getId());
            if (StrUtil.equals(ItemStateEnum.UNPLAYED.getName(),state) && CollectionUtils.isEmpty(itemStageVOS)){
                List<ItemStageVO> itemList= new ArrayList<>();
                ItemStageVO itemStageVO = new ItemStageVO();
                itemStageVO.setItemName(x.getName());
                itemStageVO.setState(ItemStateEnum.UNPLAYED.getName());
                itemStageVO.setName(ItemStateEnum.UNPLAYED.getName());
                itemList.add(itemStageVO);
                list.add(itemList);
            }else {
                if (CollectionUtils.isNotEmpty(itemStageVOS)){
                    list.add(itemStageVOS);
                }
            }
        });
        return list;
    }

    @Override
    public Boolean confirmation(Long stageId, Boolean overdue) {
        if (stageId!=null && overdue!=null){
            boolean update = this.update(new LambdaUpdateWrapper<ItemStage>().eq(ItemStage::getId, stageId).set(ItemStage::getOverdue, overdue));
            return update;
        }
        return Boolean.FALSE;
    }

    /**
     * 递归排序
     */
    private List<ItemStageVO> toSort(List<ItemStageVO> stages,List<ItemStageVO> n ,ItemStageVO stageVO){
        if (stageVO == null){
            for (ItemStageVO stage : stages){
                if (StrUtil.isBlank(stage.getHeaderId())){
                    n.add(stage);
                    toSort(stages,n,stage);
                }
            }
        }
        for (ItemStageVO stage : stages){
            if (stageVO==null){
                return n;
            }
            if (StrUtil.equals(stage.getHeaderId(),stageVO.getFooterId())){
                n.add(stage);
                toSort(stages,n,stage);
            }
        }
        return null;
    }
}
