package com.ximai.mes.pro.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ximai.common.constant.UserConstants;
import com.ximai.common.exception.ServiceException;
import com.ximai.common.utils.data.ExceptionUtil;
import com.ximai.common.utils.data.StringUtils;
import com.ximai.common.utils.sign.Md5Utils;
import com.ximai.mes.constant.WorkorderStatusEnum;
import com.ximai.mes.pro.domain.ProArrange;
import com.ximai.mes.pro.domain.ProWorkOrderArrangeRule;
import com.ximai.mes.pro.domain.proWorkOrder.ProWorkOrderArrangeRuleVal;
import com.ximai.mes.pro.domain.proWorkOrder.ProWorkorder;
import com.ximai.mes.pro.domain.vo.ProArrangeQuery;
import com.ximai.mes.pro.domain.vo.proWorkOrder.ArrangeRuleDto;
import com.ximai.mes.pro.domain.vo.proWorkOrder.WorkOrderArrangeResult;
import com.ximai.mes.pro.mapper.ProArrangeMapper;
import com.ximai.mes.pro.mapper.proWorkOrder.ProWorkorderMapper;
import com.ximai.mes.pro.service.IProArrangeService;
import com.ximai.mes.pro.service.IProWorkOrderArrangeRuleService;
import com.ximai.mes.pro.service.proWorkOrder.IProWorkOrderArrangeRuleValService;
import com.ximai.mes.pro.service.proWorkOrder.IProWorkorderService;
import com.ximai.system.strategy.AutoCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 生产编排单Service业务层处理
 *
 * @author generator
 * @date 2024-03-01
 */
@Slf4j
@Service
public class ProArrangeServiceImpl implements IProArrangeService {
    @Autowired
    private ProArrangeMapper proArrangeMapper;

    @Autowired
    private IProWorkorderService proWorkorderService;

    @Autowired
    private AutoCodeUtil autoCodeUtil;


    @Autowired
    private ProWorkorderMapper proWorkorderMapper;

    @Autowired
    private IProWorkOrderArrangeRuleService proWorkOrderArrangeRuleService;
    @Autowired
    private IProWorkOrderArrangeRuleValService proWorkOrderArrangeRuleValService;

    /**
     * 查询生产编排单
     *
     * @param arrangeId 生产编排单主键
     * @return 生产编排单
     */
    @Override
    public ProArrange selectProArrangeByArrangeId(Long arrangeId) {
        return proArrangeMapper.selectProArrangeByArrangeId(arrangeId);
    }

    @Override
    public ProArrange selectProArrangeByWorkorderId(Long workorderId) {
        return proArrangeMapper.selectProArrangeByWorkorderId(workorderId);
    }

    /**
     * 查询生产编排单列表
     *
     * @param proArrange 生产编排单
     * @return 生产编排单
     */
    @Override
    public List<ProArrange> selectProArrangeList(ProArrange proArrange) {
        return proArrangeMapper.selectProArrangeList(proArrange);
    }

    @Override
    public List<ProArrange> selectProArrangeList(QueryWrapper<ProArrange> query) {
        return proArrangeMapper.selectListByQw(query);
    }

    @Override
    public List<ProArrange> selectJoinWorkorderGroupByArrangeCode(QueryWrapper<ProArrange> query) {
        return proArrangeMapper.selectJoinWorkorderGroupByArrangeCode(query);
    }

    /**
     * 查询生产编排单列表
     *
     * @param proArrange 生产编排单
     * @return 生产编排单
     */
    @Override
    public List<ProArrange> selectProArrangeGroupList(ProArrange proArrange) {
        QueryWrapper<ProArrange> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotEmpty(proArrange.getArrangeCode()), "arrange_code", proArrange.getArrangeCode());
        queryWrapper.eq(StringUtils.isNotEmpty(proArrange.getWorkorderCode()), "workorder_code", proArrange.getWorkorderCode());
        queryWrapper.eq(StringUtils.isNotEmpty(proArrange.getArrangeSort()), "arrange_sort", proArrange.getArrangeSort());
        return proArrangeMapper.selectListGroupByQw(queryWrapper);
    }


    @Override
    @Transactional
    public String batchAddProArrange(List<ProArrange> proArranges) {
        String groupCode = autoCodeUtil.genSerialCode(ARRANGE_GROUP_CODE, "");
        List<ProWorkorder> proWorkorderList = new ArrayList<>();
        List<Long> workorderIds = proArranges.stream().map(ProArrange::getWorkorderId).collect(Collectors.toList());
        List<List<Long>> tempWorkorderIds = CollectionUtil.split(workorderIds, 500);
        for (List<Long> ids : tempWorkorderIds) {
            QueryWrapper<ProWorkorder> query = new QueryWrapper<>();
            query.in("t1.workorder_id", ids);
            List<ProWorkorder> tempList = proWorkorderService.selectListByQw(query);
            proWorkorderList.addAll(tempList);

            // 删除已有编排记录
            if (ids.size() > 0) {
                QueryWrapper<ProArrange> queryWrapper = new QueryWrapper<>();
                queryWrapper.in("workorder_id", ids);
                proArrangeMapper.deleteProArrange(queryWrapper);
            }
        }

        WorkOrderArrangeResult rst = this.arrangeExecute(proWorkorderList);
        for (List<ProWorkorder> tempList : rst.getArrangeList()) {
            String arrangeCode = autoCodeUtil.genSerialCode(UserConstants.ARRANGE_CODE, "");
            int i = 1;

            Optional<ProWorkorder> any = tempList.stream().filter(x -> x.getRequestDate() == null).findAny();
            ProWorkorder errorWorkorder = null;
            if (any.isPresent()) {
                errorWorkorder = any.get();
                ExceptionUtil.checkTrueThrowException(errorWorkorder != null, String.format("工单%s,缺少报工时间", errorWorkorder.getWorkorderCode()));
            }

            Optional<ProWorkorder> any1 = tempList.stream().filter(x -> !Objects.equals(x.getStatus(), WorkorderStatusEnum.COMBINED.getValue())).findAny();
            if (any1.isPresent()) {
                errorWorkorder = any1.get();
                ExceptionUtil.checkTrueThrowException(errorWorkorder != null, String.format("%s工单未组合，请先执行组合操作", errorWorkorder.getWorkorderCode()));
            }


            tempList.sort(Comparator.comparing(ProWorkorder::getRequestDate).thenComparing(ProWorkorder::getQuantity));

            for (ProWorkorder proWorkorder : tempList) {
                proWorkorder.setArrangeCode(arrangeCode);
                proWorkorder.setStatus(WorkorderStatusEnum.ORCHESTRATED.getValue());
                proWorkorderService.updateProWorkorder(proWorkorder);

                // 保存编排记录
                ProArrange proArrange = new ProArrange();
                proArrange.setArrangeCode(arrangeCode);
                proArrange.setArrangeSort(i);
                proArrange.setGroupCode(groupCode);
                proArrange.setWorkorderId(proWorkorder.getWorkorderId());
                proArrange.setWorkorderCode(proWorkorder.getWorkorderCode());
                this.insertProArrange(proArrange);
                i++;
            }
        }

        for (ProWorkorder proWorkorder : rst.getUnArrangeList()) {
            String arrangeCode = autoCodeUtil.genSerialCode(UserConstants.ARRANGE_CODE, "");
            proWorkorder.setArrangeCode(arrangeCode);
            proWorkorder.setStatus(WorkorderStatusEnum.ORCHESTRATED.getValue());
            proWorkorderService.updateProWorkorder(proWorkorder);

            // 保存编排记录
            ProArrange proArrange = new ProArrange();
            proArrange.setArrangeCode(arrangeCode);
            proArrange.setArrangeSort(1);
            proArrange.setGroupCode(groupCode);
            proArrange.setWorkorderId(proWorkorder.getWorkorderId());
            proArrange.setWorkorderCode(proWorkorder.getWorkorderCode());
            this.insertProArrange(proArrange);
        }
        return groupCode;
    }

    /**
     * 新增生产编排单
     *
     * @param proArrange 生产编排单
     * @return 结果
     */
    @Override
    public int insertProArrange(ProArrange proArrange) {
        proArrange.createAction();
        return proArrangeMapper.insertProArrange(proArrange);
    }

    /**
     * 修改生产编排单
     *
     * @param proArrange 生产编排单
     * @return 结果
     */
    @Override
    public int updateProArrange(ProArrange proArrange) {
        proArrange.updateAction();
        return proArrangeMapper.updateProArrange(proArrange);
    }

    /**
     * 批量删除生产编排单
     *
     * @param workorderIds 需要删除的生产编排单主键
     * @return 结果
     */
    @Transactional
    @Override
    public int deleteProArrangeByWorkorderIds(Long[] workorderIds) {
        List<Long> list = Arrays.asList(workorderIds);
        ExceptionUtil.checkTrueThrowException(CollectionUtil.isEmpty(list), JSON.toJSONString(list) + ",工单非已编排，不能进行取消组合操作");
        List<ProWorkorder> proWorkorders = proWorkorderMapper.selectListByQw(new QueryWrapper<ProWorkorder>().in("workorder_id", list));
        Set<String> workorderCodes = new HashSet<>();
        Set<String> error = new HashSet<>();
        if (CollectionUtil.isNotEmpty(proWorkorders)) {
            for (ProWorkorder proWorkorder : proWorkorders) {
                if (Objects.equals(proWorkorder.getStatus(), WorkorderStatusEnum.ORCHESTRATED.getValue())) {
                    proWorkorder.setStatus(WorkorderStatusEnum.COMBINED.getValue());
                    proWorkorder.setArrangeCode("");
                    proWorkorderMapper.updateProWorkorder(proWorkorder);
                    workorderCodes.add(proWorkorder.getWorkorderCode());
                } else {
                    error.add(proWorkorder.getWorkorderCode());
                }
            }
            ExceptionUtil.checkTrueThrowException(workorderCodes.size() != proWorkorders.size(), JSON.toJSONString(error) + ",工单非已编排，不能进行取消组合操作");
        }

        proArrangeMapper.deleteProArrange(new QueryWrapper<ProArrange>().in("workorder_code", workorderCodes));
        return 1;
    }

    /**
     * 删除生产编排单信息
     *
     * @param arrangeId 生产编排单主键
     * @return 结果
     */
    @Override
    public int deleteProArrangeByArrangeId(Long arrangeId) {
        ProArrange proArrange = proArrangeMapper.selectProArrangeByArrangeId(arrangeId);
        ProWorkorder proWorkorder = proWorkorderService.selectWorkorderById(proArrange.getWorkorderId());
        if (!WorkorderStatusEnum.ORCHESTRATED.getValue().equals(proWorkorder.getStatus())) {
            throw new ServiceException(String.format("%s只能针对编排状态工单执行执行操作", proWorkorder.getWorkorderCode()));
        }
        proWorkorderMapper.updateWorkorderStatus(proArrange.getWorkorderId(), WorkorderStatusEnum.COMBINED.getValue());
        return proArrangeMapper.deleteProArrangeByArrangeId(arrangeId);
    }


    @Override
    public WorkOrderArrangeResult arrangeExecute(List<ProWorkorder> workorderList) {
        //1.查询编排规则表，用产品类别+工序分组数据，值为合并规则属性列表
        QueryWrapper<ProWorkOrderArrangeRule> query = new QueryWrapper<>();
        Map<String, ArrangeRuleDto> ruleMap = proWorkOrderArrangeRuleService.selectProWorkOrderArrangeRule(query);
        List<Map<String, Object>> arrangeRuleVals = new ArrayList<>();
        List<Long> workorderIds = workorderList.stream().map(ProWorkorder::getWorkorderId).collect(Collectors.toList());
        List<List<Long>> workorderIdsList = CollectionUtil.split(workorderIds, 500);

        //2.查询规则属性值
        for (List<Long> ids : workorderIdsList) {
            QueryWrapper<ProWorkOrderArrangeRuleVal> queryWorkorder = new QueryWrapper<>();
            queryWorkorder.in("workorder_id", ids);
            List<Map<String, Object>> tempVals = proWorkOrderArrangeRuleValService.selectProWorkOrderArrangeRuleVal(queryWorkorder);
            arrangeRuleVals.addAll(tempVals);
        }
        return ProArrangeServiceImpl.arrangeExecute(workorderList, ruleMap, arrangeRuleVals);
    }

    @Override
    public void split(Long[] arrangeIds) {
        for (Long id : arrangeIds) {
            ProArrange proArrange = this.selectProArrangeByArrangeId(id);
            ProWorkorder proWorkorder = proWorkorderService.selectWorkorderById(proArrange.getWorkorderId());
            if (!WorkorderStatusEnum.ORCHESTRATED.getValue().equals(proWorkorder.getStatus())) {
                throw new ServiceException(String.format("%s只能针对编排状态工单执行执行操作", proWorkorder.getWorkorderCode()));
            }
            String arrangeCode = autoCodeUtil.genSerialCode(UserConstants.ARRANGE_CODE, "");
            proArrange.setArrangeCode(arrangeCode);
            proArrange.setArrangeSort(1);
            proWorkorder.setArrangeCode(arrangeCode);
            proWorkorderService.updateProWorkorder(proWorkorder);
            this.updateProArrange(proArrange);
        }
    }

    @Override
    public void deleteByWorkorderCode(String workorderCode) {
        QueryWrapper<ProArrange> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("workorder_code", workorderCode);
        proArrangeMapper.deleteProArrange(queryWrapper);
    }

    @Override
    public List<ProArrange> selectDistictArrangeList(ProArrangeQuery proArrange) {
        QueryWrapper<ProArrange> proArrangeQueryWrapper = new QueryWrapper<>();
        proArrangeQueryWrapper.in(proArrange.getWorkorderStatus() != null && proArrange.getWorkorderStatus().length > 0, "t2.status", proArrange.getWorkorderStatus());
        proArrangeQueryWrapper.eq(StringUtils.isNotEmpty(proArrange.getArrangeCode()), "t1.arrange_code", proArrange.getArrangeCode());
        proArrangeQueryWrapper.eq(StringUtils.isNotEmpty(proArrange.getCreateBy()), "t1.create_by", proArrange.getCreateBy());
        proArrangeQueryWrapper.eq(StringUtils.isNotEmpty(proArrange.getWorkorderCode()), "t2.workorder_code", proArrange.getWorkorderCode());
        List<ProArrange> proArranges = proArrangeMapper.selectDistictArrangeList(proArrangeQueryWrapper);
        Set<String> objects = new HashSet<>();
        return proArranges.stream().filter(x -> objects.add(x.getArrangeCode())).collect(Collectors.toList());
    }

    @Override
    public void appendArrange(String arrangeCode, Long[] workorderIds) {
        ProArrange proArrangeQuery = new ProArrange();
        proArrangeQuery.setArrangeCode(arrangeCode);
        List<ProArrange> list = proArrangeMapper.selectProArrangeList(proArrangeQuery);
        if (list.size() == 0) {
            throw new ServiceException(String.format("未找到编排单号数据：%s", arrangeCode));
        }

        Integer max = list.stream().map(ProArrange::getArrangeSort).max(Comparator.comparing(Integer::valueOf)).get();
        for (Long id : workorderIds) {
            max++;
            ProWorkorder proWorkorder = proWorkorderService.selectWorkorderById(id);
            if (!WorkorderStatusEnum.COMBINED.getValue().equals(proWorkorder.getStatus())) {
                throw new ServiceException(String.format("%s工单未组合，请先执行组合操作", proWorkorder.getWorkorderCode()));
            }
            proWorkorder.setArrangeCode(arrangeCode);
            proWorkorder.setStatus(WorkorderStatusEnum.ORCHESTRATED.getValue());
            proWorkorderService.updateProWorkorder(proWorkorder);
            ProArrange proArrange = new ProArrange();
            proArrange.setArrangeCode(arrangeCode);
            proArrange.setArrangeSort(max);
            proArrange.setGroupCode(list.get(0).getGroupCode());
            proArrange.setWorkorderId(proWorkorder.getWorkorderId());
            proArrange.setWorkorderCode(proWorkorder.getWorkorderCode());
            this.insertProArrange(proArrange);
        }
    }

    private static WorkOrderArrangeResult arrangeExecute(List<ProWorkorder> workorderList,
                                                         Map<String, ArrangeRuleDto> ruleMap,
                                                         List<Map<String, Object>> arrangeRuleVals) {

        Map<Long, ProWorkorder> workorderMap = workorderList.stream().collect(Collectors.toMap(ProWorkorder::getWorkorderId, s -> s));
        WorkOrderArrangeResult rst = new WorkOrderArrangeResult();
        Map<Long, ProWorkorder> unArrangeList = new HashMap<>();//未编排工单
        //工单对应规则组
        Map<Long, Set<String>> workorderRuleGroupMap = new HashMap<>();
        //工单工序对应规则属性值
        Map<String, Map<String, Object>> arrangeRuleValMap = new HashMap<>();
        //1.统计工单满足所有编排规则
        arrangeRuleVals.forEach(s -> {
            Long workorderId = (Long) s.get("workorder_id");
            String ruleKey = s.get("item_type_name") + (String) s.get("process_name");
            if (ruleMap.containsKey(ruleKey)) {
                if (!workorderRuleGroupMap.containsKey(workorderId)) {
                    workorderRuleGroupMap.put(workorderId, new LinkedHashSet<>());
                }
                workorderRuleGroupMap.get(workorderId).add(ruleKey);
                arrangeRuleValMap.put(workorderId + ruleKey, s);
            }
        });

        workorderList.forEach(s -> {
            if (!workorderRuleGroupMap.containsKey(s.getWorkorderId())) {
                unArrangeList.put(s.getWorkorderId(), s);
            }
        });

        //2.工单按规则组分组，key=多个规则键(产品类别+工序)以,拼接
        Map<String, List<ProWorkorder>> workorderGroup = new HashMap<>();
        workorderRuleGroupMap.forEach((k, v) -> {
            String key = v.stream().collect(Collectors.joining(","));
            ProWorkorder workorder = workorderMap.get(k);
            if (!workorderGroup.containsKey(key)) {
                workorderGroup.put(key, new ArrayList<>());
            }
            workorderGroup.get(key).add(workorder);
        });
        Map<String, List<ProWorkorder>> tempMap = new HashMap<>();
        //3.根据合并规则合并相同工单
        List<List<ProWorkorder>> workorderArrangeList = new ArrayList<>();
        workorderGroup.forEach((k, v) -> {
            if (v.size() == 1) {
                ProWorkorder proWorkorder = v.get(0);
                unArrangeList.put(proWorkorder.getWorkorderId(), proWorkorder);
                return;
            }
            tempMap.clear();

            //工单对应规则的唯一值
            Map<Long, String> workorderRuleUniqueMap = new HashMap<>();
            v.forEach(s -> {
                String uniqueUnion = "";
                for (String mapKey : k.split(",")) {
                    //计算订单规则组的值唯一值（每组规则值MD5后相加后再次MD5）
                    List<String> arrangeRule = ruleMap.get(mapKey).getRuleMap();
                    Map<String, Object> arrangeRuleVal = arrangeRuleValMap.get(s.getWorkorderId() + mapKey);
                    String unique = ProArrangeServiceImpl.getWorkorderUnique(arrangeRule, arrangeRuleVal);
                    uniqueUnion += unique;
                }
                workorderRuleUniqueMap.put(s.getWorkorderId(), uniqueUnion);
            });

            //相同唯一值的工单合为一组
            workorderRuleUniqueMap.forEach((k2, v2) -> {
                if (!tempMap.containsKey(v2)) {
                    tempMap.put(v2, new ArrayList<>());
                }
                ProWorkorder workorder = workorderMap.get(k2);
                tempMap.get(v2).add(workorder);
            });

            //记录结果
            tempMap.forEach((k2, v2) -> {
                if (v2.size() == 1) {
                    ProWorkorder proWorkorder = v2.get(0);
                    unArrangeList.put(proWorkorder.getWorkorderId(), proWorkorder);
                    return;
                }
                workorderArrangeList.add(v2);
            });
        });
        //根据间隔天数规则重新分组
        List<List<ProWorkorder>> workorderArrangeListNew = ProArrangeServiceImpl.deliveryIntervalExecute(workorderArrangeList,
                workorderRuleGroupMap,
                ruleMap);
        rst.setArrangeList(workorderArrangeListNew);
        rst.setUnArrangeList(new ArrayList<>(unArrangeList.values()));
        return rst;
    }

    private static String getWorkorderUnique(List<String> arrangeRule, Map<String, Object> arrangeRuleVal) {
        StringBuffer rst = new StringBuffer();
        arrangeRule.forEach(column -> {
            rst.append(arrangeRuleVal.get(column));
        });
        return Md5Utils.hash(rst.toString());
    }

    //根据编排间隔天数再次分组
    private static List<List<ProWorkorder>> deliveryIntervalExecute(List<List<ProWorkorder>> data,
                                                                    Map<Long, Set<String>> workorderRuleGroupMap,
                                                                    Map<String, ArrangeRuleDto> ruleMap){
        List<List<ProWorkorder>> rst = new ArrayList<List<ProWorkorder>>();
        data.forEach(s->{
            String ruleKey =workorderRuleGroupMap.get(s.get(0).getWorkorderId()).stream().findFirst().get();
            ArrangeRuleDto ruleDto = ruleMap.get(ruleKey);
            if(ruleDto!=null&&ruleDto.getWorkOrderArrangeRule()!=null){
                rst.addAll(ProArrangeServiceImpl.deliveryIntervalExecute(s, ruleDto.getWorkOrderArrangeRule().getRequestDateInterval(),
                        ruleDto.getWorkOrderArrangeRule().getRequestDateIntervalMinNum()));
            }else{
                rst.add(s);
            }
        });
        return rst;
    }

    private static List<List<ProWorkorder>> deliveryIntervalExecute(List<ProWorkorder> data,
                                                                    Integer intervalDays,
                                                                    Integer intervalDaysMinNum) {
        data.sort(Comparator.comparing(ProWorkorder::getRequestDate));
        List<List<ProWorkorder>> rst = new ArrayList<List<ProWorkorder>>();
        if(intervalDays==null||intervalDays<=0){
            rst.add(data);
            return rst;
        }
        //选出小于最小数量的工单，作业第一组数据
        List<ProWorkorder> lessNumOrders = new ArrayList<ProWorkorder>();
        AtomicReference<LocalDateTime> firstRequestDate = new AtomicReference<LocalDateTime>();
        List<ProWorkorder> tempGruop = new ArrayList<ProWorkorder>();
        data.forEach(s->{
            if(intervalDaysMinNum!=null&&intervalDaysMinNum>0&&s.getQuantity().compareTo(new BigDecimal(intervalDaysMinNum))<0){
                lessNumOrders.add(s);
            }else{
                //首工单
                if(firstRequestDate.get() ==null){
                    firstRequestDate.set(DateUtil.toLocalDateTime(s.getRequestDate()));
                    tempGruop.add(s);
                    return;
                }
                Duration duration = Duration.between(firstRequestDate.get(), DateUtil.toLocalDateTime(s.getRequestDate()));
                if(duration.toDays()<intervalDays){
                    tempGruop.add(s);
                }else{
                    rst.add(BeanUtil.copyToList(tempGruop, ProWorkorder.class));
                    //清空分组信息
                    firstRequestDate.set(DateUtil.toLocalDateTime(s.getRequestDate()));
                    tempGruop.clear();
                    tempGruop.add(s);
                }
            }
        });
        if(tempGruop.size()>0){
            rst.add(BeanUtil.copyToList(tempGruop, ProWorkorder.class));
        }
        rst.get(0).addAll(lessNumOrders);
        return rst;
    }

}
