package com.prolog.eis.service.buildwave.impl;

import com.prolog.eis.constants.base.ContainerTaskStatusConstants;
import com.prolog.eis.constants.sys.SysParamConstants;
import com.prolog.eis.constants.wms.WmsConstants;
import com.prolog.eis.dao.out.BizOutboundTaskMapper;
import com.prolog.eis.dao.out.shelves.GridInfoMapper;
import com.prolog.eis.dao.out.shelves.sys.SysParamMapper;
import com.prolog.eis.dto.buildwave.BuildWaveDispatchDto;
import com.prolog.eis.dto.buildwave.PObContainerStoreDto;
import com.prolog.eis.dto.buildwave.PObOrderDetailDto;
import com.prolog.eis.dto.buildwave.PObOrderDto;
import com.prolog.eis.dto.dispatch.OrderItemCountDto;
import com.prolog.eis.dto.strategy.ContainerInfoDto;
import com.prolog.eis.dto.strategy.OrderDetailDto;
import com.prolog.eis.dto.strategy.OrderDto;
import com.prolog.eis.enums.base.BaseStatusEnum;
import com.prolog.eis.model.container.ContainerTask;
import com.prolog.eis.model.outbound.BizOutboundTask;
import com.prolog.eis.model.shelves.GridInfo;
import com.prolog.eis.model.sys.SysParam;
import com.prolog.eis.router.vo.ContainerLocationVo;
import com.prolog.eis.service.buildwave.BuildWaveInitService;
import com.prolog.eis.service.feign.RouteFeignService;
import com.prolog.eis.service.logic.LogicService;
import com.prolog.eis.service.out.ContainerInfoService;
import com.prolog.eis.service.out.ContainerTaskService;
import com.prolog.eis.service.sys.SysParamService;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.utils.MapUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.prolog.eis.constants.base.BaseBillTypeConstants.OUTBOUND_TYPE;
import static com.prolog.eis.constants.sys.SysParamConstants.TOTAL_GRID_COUNT;


/**
 * @Version: 1.0
 * @Author: wuhan
 * @Created: 2024/7/30 15:08
 */
@Service
@Log4j2
@RequiredArgsConstructor
public class BuildWaveInitServiceImpl implements BuildWaveInitService {

    private final LogicService logicService;

    private final ContainerTaskService containerTaskService;

    private final ContainerInfoService containerInfoService;

    private final SysParamMapper sysParamMapper;

    private final BizOutboundTaskMapper bizOutboundTaskMapper;

    private final GridInfoMapper gridInfoMapper;

    private final RouteFeignService routeFeignService;

    private final SysParamService sysParamService;

    @Override
    public BuildWaveDispatchDto init() throws Exception {
        // 获取单个波次最大订单数
        List<SysParam> maxWaveOrderCountParamList = sysParamMapper.findByMap(MapUtils.put("paramNo", SysParamConstants.MAX_WAVE_ORDER_COUNT).getMap(), SysParam.class);
        if (CollectionUtils.isEmpty(maxWaveOrderCountParamList)) {
            log.warn("拆零初始化无最大波次订单数");
            return null;
        }
        SysParam maxWaveOrderCountParam = maxWaveOrderCountParamList.get(0);
        int maxWaveOrderCount = Integer.parseInt(maxWaveOrderCountParam.getParamValue());

        // 校验检查
        boolean check = check();
        if(!check){
            log.warn("拆零订单数不满足组单需求，校验检查失败！");
            return null;
        }
        int maxTotalCount = Math.min(maxWaveOrderCount, TOTAL_GRID_COUNT * 2);

        // 获取订单
        List<OrderDto> outboundTasks = logicService.getCreateOutboundTaskLS(String.valueOf(OUTBOUND_TYPE), WmsConstants.AGVL);
        if(CollectionUtils.isEmpty(outboundTasks)){
            return null;
        }
        // 判断是否尾波处理
        int tailWaveSwitch = 0;
        List<SysParam> tailWaveSwitchParams = sysParamMapper.findByMap(MapUtils.put("paramNo", SysParamConstants.TAIL_WAVE_SWITCH).getMap(), SysParam.class);
        if (CollectionUtils.isEmpty(tailWaveSwitchParams)) {
            if(outboundTasks.size() < maxTotalCount){
                log.warn("拆零订单数不满足组单需求，订单数{}", outboundTasks.size());
                return null;
            }
        }else{
            SysParam tailWaveSwitchParam = tailWaveSwitchParams.get(0);
            tailWaveSwitch = Integer.parseInt(tailWaveSwitchParam.getParamValue());
            if(tailWaveSwitch == 0){
                if(outboundTasks.size() < maxTotalCount){
                    log.warn("拆零订单数不满足组单需求，订单数{}", outboundTasks.size());
                    return null;
                }
            }

        }
        // 组装数据
        BuildWaveDispatchDto initDto = new BuildWaveDispatchDto();
        initDto.setMaxWaveOrderCount(maxTotalCount);
        initDto.setTailWaveSwitch(tailWaveSwitch);
        List<PObOrderDto> orderList = getpObOrderDtos(outboundTasks);
        initDto.setOrderList(orderList);
        // 获取容器库存
        List<ContainerInfoDto> containerInfoDtoList = getPartStrategyContainerInfo(WmsConstants.AGVL);
        if (org.apache.commons.collections4.CollectionUtils.isEmpty(containerInfoDtoList)) {
            log.warn("拆零初始化无可用库存");
            return null;
        }
        List<PObContainerStoreDto> containerStoreList = containerInfoDtoList.stream().map(containerInfoDto -> {
            PObContainerStoreDto pobContainerStoreDto = new PObContainerStoreDto();
            pobContainerStoreDto.setContainerNo(containerInfoDto.getContainerSubNo());
            pobContainerStoreDto.setItemId(containerInfoDto.getItemId());
            pobContainerStoreDto.setLotId(containerInfoDto.getLotId());
            pobContainerStoreDto.setOwnerId(containerInfoDto.getOwnerId());
            pobContainerStoreDto.setPackId(containerInfoDto.getPackId());
            pobContainerStoreDto.setUseQty(containerInfoDto.getQty());
            return pobContainerStoreDto;
        }).collect(Collectors.toList());
        initDto.setContainerStoreList(containerStoreList);
        return initDto;
    }

    private List<PObOrderDto> getpObOrderDtos(List<OrderDto> outboundTasks) {
        List<PObOrderDto> orderList = new ArrayList<>();
        for(OrderDto orderDto : outboundTasks){
            PObOrderDto pobOrderDto = new PObOrderDto();
            pobOrderDto.setOrderId(orderDto.getOrderId());
            List<PObOrderDetailDto> detailList = new ArrayList<>();
            List<OrderDetailDto> orderDetailDtoList = orderDto.getOrderDetailDtoList();
            for(OrderDetailDto orderDetail : orderDetailDtoList){
                PObOrderDetailDto pobOrderDetailDto = new PObOrderDetailDto();
                String storeKey = String.format("%s@@%s@@%s@@%s",
                        orderDetail.getOwnerId(),
                        orderDetail.getItemId(),
                        orderDetail.getLotId(),
                        orderDetail.getPackId());
                pobOrderDetailDto.setItemNo(storeKey);
                pobOrderDetailDto.setPlanNum(orderDetail.getPlanNum());
                pobOrderDetailDto.setOrderId(orderDto.getOrderId());
                pobOrderDetailDto.setOrderDetailId(orderDetail.getOrderDetailId());
                detailList.add(pobOrderDetailDto);
            }
            pobOrderDto.setDetailList(detailList);
            orderList.add(pobOrderDto);
        }
        return orderList;
    }

    public List<ContainerInfoDto> getPartStrategyContainerInfo(String warehouseArea) throws Exception {
        Criteria criteria = new Criteria(ContainerTask.class);
        criteria.restrictionBuilder().eq(ContainerTask::getWmsLocationCode, warehouseArea).build();
        List<ContainerTask> containerTaskList = containerTaskService.findByCriteria(criteria);
        List<ContainerInfoDto> containerInfoDtoList = containerInfoService.getContainerInfoLsDto(warehouseArea);

        // 判断四向库在途库存开关是否打开
        if (sysParamService.findVal(SysParamConstants.SXK_TO_AGV_STOCK_SWITCH, "0").equals("0")) {
            System.out.println("AGV拆零四向库库存预占开关关闭");
            List<String> containerNoList = containerInfoDtoList.stream().map(ContainerInfoDto::getContainerNo).collect(Collectors.toList());
            List<ContainerLocationVo> data = routeFeignService.findLocationByContainerList(containerNoList);
            // 查询四向库发往AGV库的在途库存
            List<ContainerLocationVo> sxkContainerLocations = data.stream().filter(containerLocationVo -> containerLocationVo.getSourceArea().contains("SX")).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(sxkContainerLocations)){
                // 按照容器号分组
                Map<String, List<ContainerLocationVo>> collect = sxkContainerLocations.stream().collect(Collectors.groupingBy(ContainerLocationVo :: getContainerNo));
                containerInfoDtoList.removeIf(t -> collect.containsKey(t.getContainerNo()));
            }
        }

        List<String> full = containerTaskList.stream().filter(s -> s.getContainerType() == ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE || s.getContainerType() == ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE).map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
//        List<String> containerNoList = containerTaskList.stream().filter(s -> s.getStatus() >= 2).map(ContainerTask :: getContainerNo).distinct().collect(Collectors.toList());
        containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !full.contains(containerInfoDto.getContainerSubNo())).collect(Collectors.toList());
//        containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !containerNoList.contains(containerInfoDto.getContainerNo())).collect(Collectors.toList());
        Map<String, BigDecimal> collect = containerTaskList.stream().collect(Collectors.toMap(ContainerTask::getContainerSubNo, ContainerTask::getQty, BigDecimal::add));
        for (ContainerInfoDto containerInfoDto : containerInfoDtoList) {
            BigDecimal qty = collect.get(containerInfoDto.getContainerSubNo());
            if (Objects.nonNull(qty)) {
                containerInfoDto.setQty(containerInfoDto.getQty().subtract(qty));
            }
        }
        return containerInfoDtoList;
    }

    private boolean check(){
        // 检查是否有订单需要组波次
        Criteria c1 = Criteria.forClass(BizOutboundTask.class);
        c1.restrictionBuilder()
                .eq(BizOutboundTask :: getBillType, String.valueOf(OUTBOUND_TYPE))
                .eq(BizOutboundTask :: getStatus, 0)
                .eq(BizOutboundTask :: getWmsLocationCode, WmsConstants.AGVL)
                .build();
        List<BizOutboundTask> notStartedOrders = bizOutboundTaskMapper.findByCriteria(c1);
        if (CollectionUtils.isEmpty(notStartedOrders)) {
            System.out.println("未查询到需要组波的订单");
            return false;
        }
        // 1、查询已生成波次号并且状态是创建则不进行组单
//        Criteria c2 = Criteria.forClass(BizOutboundTask.class);
//        c2.restrictionBuilder()
//                .eq(BizOutboundTask :: getBillType, String.valueOf(OUTBOUND_TYPE))
//                .eq(BizOutboundTask :: getStatus, 0)
//                .eq(BizOutboundTask :: getWmsLocationCode, WmsConstants.AGVL)
//                .isNotNull(BizOutboundTask :: getEisWaveNo)
//                .build();
        List<BizOutboundTask> startedOrders = notStartedOrders.stream().filter(t -> StringUtils.isNotEmpty(t.getEisWaveNo())).collect(Collectors.toList());
//        List<BizOutboundTask> startedOrders = bizOutboundTaskMapper.findByCriteria(c2);
        if(CollectionUtils.isNotEmpty(startedOrders)){
            System.out.printf("已生成波次号并且状态是创建则不进行组单，波次号：" + startedOrders.get(0).getEisWaveNo());
            return false;
        }
        // 2、查询已生成波次号，已生成命中库存总检，未绑定格口如果存在则不进行组单
        if(sysParamService.findVal(SysParamConstants.CHECK_GRID_BINDING_SWITCH, "0").equals("0")){
            Criteria c3 = Criteria.forClass(BizOutboundTask.class);
            c3.restrictionBuilder()
                    .eq(BizOutboundTask :: getBillType, String.valueOf(OUTBOUND_TYPE))
                    .eq(BizOutboundTask :: getWmsLocationCode, WmsConstants.AGVL)
                    .isNotNull(BizOutboundTask :: getEisWaveNo)
                    .eq(BizOutboundTask :: getIsBindGrid,0)
                    .build();
            List<BizOutboundTask> bindGridOrders = bizOutboundTaskMapper.findByCriteria(c3);
            if(CollectionUtils.isNotEmpty(bindGridOrders)){
                System.out.println("已生成波次号，有未绑定格口如果存在则不进行组单" + bindGridOrders.get(0).getEisWaveNo());
                return false;
            }
        }

        // 3、查询当前格口绑定订单，是否有未开始的搬运任务，有则不进行组单
        Criteria c4 = Criteria.forClass(BizOutboundTask.class);
        c4.restrictionBuilder()
                .eq(BizOutboundTask :: getBillType, String.valueOf(OUTBOUND_TYPE))
                .eq(BizOutboundTask :: getWmsLocationCode, WmsConstants.AGVL)
                .isNotNull(BizOutboundTask :: getEisWaveNo)
                .ge(BizOutboundTask :: getStatus, BaseStatusEnum.STATUS_STARTED.getCode())
                .build();
        List<BizOutboundTask> carryOrders = bizOutboundTaskMapper.findByCriteria(c4);
        List<Integer> ids = carryOrders.stream().map(BizOutboundTask::getId).collect(Collectors.toList());

        Criteria c5 = Criteria.forClass(ContainerTask.class);
        c5.restrictionBuilder()
                .eq(ContainerTask :: getStatus, ContainerTaskStatusConstants.INIT_STATUS)
                .in(ContainerTask :: getOutboundId, ids)
                .build();
        List<ContainerTask> initContainerTasks = containerTaskService.findByCriteria(c5);
        if(CollectionUtils.isNotEmpty(initContainerTasks)){
            List<String> containerTasks = initContainerTasks.stream().map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
            List<SysParam> sysParams = sysParamMapper.findByMap(MapUtils.put("paramNo", SysParamConstants.NUMBER_OF_TRAY_TASKS).getMap(), SysParam.class);
            int paramValue;
            if (CollectionUtils.isEmpty(sysParams)) {
                paramValue = 5;
            }else{
                paramValue = Integer.parseInt(sysParams.get(0).getParamValue());
            }
            if(containerTasks.size() - paramValue > 0){
                System.out.println("无法组单，未开始的容器任务数：" +containerTasks.size() + "-剩余托盘激活数：" + paramValue + "> 0");
                return false;
            }

        }
        return true;
    }
}
