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

import com.prolog.eis.constants.base.BaseBillTypeConstants;
import com.prolog.eis.constants.base.BaseLockConstants;
import com.prolog.eis.constants.base.ContainerTaskStatusConstants;
import com.prolog.eis.constants.redisKey.RedisKey;
import com.prolog.eis.constants.sys.SysParamConstants;
import com.prolog.eis.constants.wms.WmsConstants;
import com.prolog.eis.dao.out.OrderLogMapper;
import com.prolog.eis.dto.item.ItemSyncVO;
import com.prolog.eis.dto.shelves.GroupGridDto;
import com.prolog.eis.dto.shelves.InitDataDto;
import com.prolog.eis.dto.shelves.OutSaleDetailDto;
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.outbound.BizOutboundTaskDetail;
import com.prolog.eis.model.outbound.OrderLog;
import com.prolog.eis.model.outbound.PrintTask;
import com.prolog.eis.model.shelves.GridInfo;
import com.prolog.eis.model.shelves.GridInfoGroup;
import com.prolog.eis.service.feign.FeignGoodsService;
import com.prolog.eis.service.feign.RouteFeignService;
import com.prolog.eis.service.logic.LogicService;
import com.prolog.eis.service.out.*;
import com.prolog.eis.service.shelves.GridInfoGroupService;
import com.prolog.eis.service.shelves.GridInfoService;
import com.prolog.eis.service.shelves.ItemService;
import com.prolog.eis.service.stationOutbound.StationObDispatchService;
import com.prolog.eis.service.sys.SysParamService;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

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

/**
 * @author zmr
 * @description: 出库任务表-服务实现类
 * @date 2023-07-06 11:36
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LogicServiceImpl implements LogicService {

    private final BizOutboundTaskService bizOutboundTaskService;
    private final BizOutboundTaskDetailService bizOutboundTaskDetailService;
    private final ContainerTaskService containerTaskService;
    private final GridInfoService gridInfoService;
    private final GridInfoGroupService gridInfoGroupService;
    private final ItemService itemService;
    private final ContainerInfoService containerInfoService;
    private final BizOutboundTaskDetailDismantleService bizOutboundTaskDetailDismantleService;
    private final StationObDispatchService stationObDispatchService;
    private final RouteFeignService plgFeignRouteService;
    private final SysParamService sysParamService;
    private final PrintTaskService printTaskService;
    private final OrderLogMapper orderLogMapper;
    private final RedisTemplate redisTemplate;
    private final FeignGoodsService feignGoodsService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean bindGirl() throws Exception {
        //初始化数据
        InitDataDto initDataDto = initData();
        if (CollectionUtils.isEmpty(initDataDto.getOutSaleMList())) {
            log.info("订单绑定格口：无待绑定格口订单");
            return false;
        }
        if (CollectionUtils.isEmpty(initDataDto.getDicGroups())) {
            log.info("订单绑定格口：无空闲格口");
            return false;
        }
        //获取最优格口
        GridInfo bestGridDto = getBestGrid(initDataDto);

        //获取最优订单
        OutSaleDetailDto orderDto = getBestOrder(initDataDto);

        //订单、格口绑定
        binding(bestGridDto, orderDto);
        return true;
    }


    private void binding(GridInfo bestGridDto, OutSaleDetailDto orderDto) {
        AssertUtils.isNotTrue(!redisTemplate.opsForValue().setIfAbsent(RedisKey.GRID_BIND_KEY + bestGridDto.getGridNo(), bestGridDto.getGridNo()), bestGridDto.getGridNo() + "格口存在并发重新获取格口");
        Criteria criteria = new Criteria(GridInfo.class);
        criteria.setRestriction(Restrictions.eq("gridNo", bestGridDto.getGridNo()));
        gridInfoService.updateMapByCriteria(MapUtils.put("orderId", orderDto.getOrderId()).put("billFlag", 1).put("lastFlag", 1).getMap(), criteria);
        criteria = new Criteria(BizOutboundTask.class);
        criteria.setRestriction(Restrictions.eq("id", orderDto.getOrderId()));
        bizOutboundTaskService.updateMapByCriteria(MapUtils.put("isBindGrid", WmsConstants.bind).put("bindTime", new Date()).put("extend2", 2).getMap(), criteria);
        BizOutboundTask outboundTask = bizOutboundTaskService.findById(orderDto.getOrderId());
        orderLogMapper.save(OrderLog.builder()
                .orderId(orderDto.getOrderId().toString())
                .orderNo(outboundTask.getBillNo())
                .type("格口绑定")
                .remark("绑定格口号：" + bestGridDto.getGridNo())
                .createTime(new Date())
                .build());
        //修改绑定时间
//        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date());
        sysParamService.updateSysParam(SysParamConstants.GIRL_BIND_TIME, String.valueOf(System.currentTimeMillis()));
        //回告wms绑定信息
        callWmsBind(bestGridDto, orderDto);
    }

    private void callWmsBind(GridInfo bestGridDto, OutSaleDetailDto orderDto) {
        PrintTask printTask = new PrintTask();
        printTask.setPrintCount(0);
        printTask.setGridNo(bestGridDto.getGridNo());
        printTask.setCreateTime(new Date());
        printTask.setStatus(0);
        printTask.setWmsOutTaskId(orderDto.getOrderId());
        printTask.setType(2);
        printTaskService.save(printTask);
    }

    private GridInfo getBestGrid(InitDataDto initDataDto) {
        //最优格口组（空闲格口最多-》分组序号最小）
        List<GroupGridDto> groupGridDtos = initDataDto.getDicGroups().stream().sorted(Comparator.comparing(GroupGridDto::getUsableGK, Comparator.reverseOrder())
                .thenComparing(GroupGridDto::getGroupId)).collect(Collectors.toList());
        GroupGridDto groupGridDto = groupGridDtos.get(0);
        //根据格口序号排序
        List<GridInfo> gridInfoList = groupGridDto.getGridInfoList().stream().filter(item -> Objects.isNull(item.getOrderId())).sorted(Comparator.comparing(GridInfo::getGridNo)).collect(Collectors.toList());
        return gridInfoList.get(0);
    }

    private boolean checkGrid(InitDataDto initDataDto) {
        List<GroupGridDto> dicGroups = initDataDto.getDicGroups();
        boolean flag = false;
        for (GroupGridDto dicGroupDto : dicGroups) {
            List<GridInfo> dicSowingwallList = dicGroupDto.getGridInfoList();
            //找到组内空闲格口
            long usableGK = dicSowingwallList.stream()
                    .filter(s -> s.getOrderId() == null).count();
            dicGroupDto.setUsableGK(Integer.parseInt(usableGK + ""));
            //找到组内正在执行的任务
            long userWork = dicSowingwallList.stream()
                    .filter(s -> s.getOrderId() != null).count();
            dicGroupDto.setUserWork(Integer.parseInt(userWork + ""));
        }
        Iterator<GroupGridDto> iterator = dicGroups.iterator();
        while (iterator.hasNext()) {
            GroupGridDto dicGroupDto = iterator.next();
            if (dicGroupDto.getUsableGK() == 0) {
                iterator.remove();
            }
        }
        if (dicGroups.size() > 0) {
            flag = true;
        }

        return flag;
    }

    /**
     * 获取最优订单
     *
     * @param initDataDto
     * @return
     */
    private OutSaleDetailDto getBestOrder(InitDataDto initDataDto) {
        //订单匹配结果汇总
        List<OutSaleDetailDto> outSaleDetails = new ArrayList<>();
        //已下发的订单的商品汇总
        List<String> waveExecSpids = initDataDto.getItemIds();

        //库存满足的订单
        List<OrderDto> outSaleMList = initDataDto.getOutSaleMList();
        Integer salesOvertime = Integer.parseInt(String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.SALES_OVERTIME)));

        for (OrderDto outSaleMDto : outSaleMList) {
            List<OrderDetailDto> outSaleBillingDList = outSaleMDto.getOrderDetailDtoList();
            int matchCount = 0;
            int notMatchCount = 0;
            for (OrderDetailDto outSaleBillingD : outSaleBillingDList) {
                if (waveExecSpids.contains(outSaleBillingD.getItemId())) {
                    matchCount++;
                } else {
                    notMatchCount++;
                }
            }
            OutSaleDetailDto outSaleDetail = new OutSaleDetailDto();
            outSaleDetail.setOrderId(outSaleMDto.getOrderId());
            outSaleDetail.setBillNo(outSaleMDto.getBillNo());
            outSaleDetail.setWaveNo(outSaleMDto.getWaveNo());
            outSaleDetail.setPriority(outSaleMDto.getPriority());
            outSaleDetail.setSalesOvertime(Duration.between(LocalDateTime.now(), outSaleMDto.getOrderCreateTime()).toMinutes() > salesOvertime ? 1 : 0);
            outSaleDetail.setMatchCount(matchCount);
            outSaleDetail.setNotMatchCount(notMatchCount);
            outSaleDetails.add(outSaleDetail);
        }
        //判断重复度
        OutSaleDetailDto outSaleDetailDto = getMaxOutSaleDetail(outSaleDetails, initDataDto);
        return outSaleDetailDto;
    }


    //获取波次优先、重复度最大，非重复度最小的订单
    private OutSaleDetailDto getMaxOutSaleDetail(List<OutSaleDetailDto> list, InitDataDto initDataDto) {

        //优先波次号、订单优先级、订单品批重复度、订单品批不匹配数量
        list = list.stream().sorted(Comparator.comparing(OutSaleDetailDto::getPriority)
                .thenComparing(OutSaleDetailDto::getSalesOvertime, Comparator.reverseOrder())
                .thenComparing(OutSaleDetailDto::getMatchCount, Comparator.reverseOrder())
                .thenComparing(OutSaleDetailDto::getNotMatchCount)).collect(Collectors.toList());
        return list.get(0);
    }

    private InitDataDto initData() throws Exception {
        InitDataDto initData = new InitDataDto();
        //获取未下发的订单集合
        List<OrderDto> orderDtos = getOutboundTaskByBillType(String.valueOf(BaseBillTypeConstants.OUTBOUND_TYPE), WmsConstants.AGVL, WmsConstants.notBind);

        initData.setOutSaleMList(orderDtos);

        List<OrderDetailDto> detailList = bizOutboundTaskDetailService.findOutDetailList(String.valueOf(BaseBillTypeConstants.OUTBOUND_TYPE), WmsConstants.AGVL, WmsConstants.bind + "");
        List<String> itemIds = detailList.stream().filter(o -> o.getStatus() > 10 && o.getStatus() < 20).map(o -> o.getItemId()).distinct().collect(Collectors.toList());
        //查询已下发的商品、批次
        initData.setItemIds(itemIds);
        //格口分组
        List<GroupGridDto> gridList = getGridList();
        initData.setDicGroups(gridList);
        return initData;
    }


    @Override
    public void updateContainerType(Integer containerType, String containerNo) {
        if (containerType == ContainerTaskStatusConstants.HALF_TRAY_TYPE) {
            Long count = containerTaskService.findCountByMap(MapUtils.put("containerNo", containerNo).put("containerType", ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE).getMap());
            if (count > 0) {
                Criteria criteria = new Criteria(ContainerTask.class);
                criteria.setRestriction(Restrictions.eq("containerNo", containerNo));
                containerTaskService.updateMapByCriteria(MapUtils.put("containerType", ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE).getMap(), criteria);
            }
        }
    }

    /**
     * 更新出库明细到位数量和状态.
     * 1.更新出库明细实际商品数
     * 2.更新容器任务状态
     *
     * @param collectList
     */
    private void doUpdateOutTaskDetail(List<ContainerTask> collectList) throws Exception {
        for (ContainerTask containerTask : collectList) {
            BizOutboundTaskDetail outTaskDetail = bizOutboundTaskDetailService.findById(containerTask.getOutboundDetailId());
            if (outTaskDetail == null) {
                throw new Exception("出库任务明细不存在，任务ID：" + containerTask.getOutboundDetailId());
            }
            //更新出库任务状态
            outTaskDetail.setActQty(new BigDecimal(outTaskDetail.getActQty() + "").add(new BigDecimal(containerTask.getQty() + "")));
            outTaskDetail.setUpdateTime(LocalDateTime.now());
            if (outTaskDetail.getActQty().compareTo(outTaskDetail.getPlanQty()) >= 0) {
                outTaskDetail.setStatus(BaseStatusEnum.STATUS_CARRY.getCode());
            }
            bizOutboundTaskDetailService.update(outTaskDetail);
        }
    }

    @Override
    public Long additionalTaskLogic(String containerNo) {
        Criteria criteria = Criteria.forClass(ContainerTask.class);
        criteria.restrictionBuilder().gt(ContainerTask::getStatus, BaseStatusEnum.STATUS_INIT.getCode())
                .eq(ContainerTask::getContainerNo, containerNo).build();
        Long count = containerTaskService.findCountByCriteria(criteria);
        return count;
    }


    @Override
    public List<OrderDto> getOutboundTask(String billType, String warehouseArea) throws Exception {
        //eis组单调度开关
        String assemblyOrderSwitch = String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.ASSEMBLY_ORDER_SWITCH));

        List<OrderDto> orderDtoList = bizOutboundTaskService.getCreateOutboundTaskList(billType, warehouseArea);
        if (orderDtoList.isEmpty()) {
            return null;
        }

        List<OrderDetailDto> orderDetailDtoList = bizOutboundTaskDetailService.findAddOutDetailList(billType, warehouseArea, assemblyOrderSwitch);
        if (orderDetailDtoList.isEmpty()) {
            return null;
        }
        Map<Integer, List<OrderDetailDto>> collect = orderDetailDtoList
                .stream().collect(Collectors.groupingBy(OrderDetailDto::getOrderId));

        Iterator<OrderDto> iterator = orderDtoList.iterator();
        while (iterator.hasNext()) {
            OrderDto orderDto = iterator.next();
            if (!collect.containsKey(orderDto.getOrderId())) {
                iterator.remove();
            }
            orderDto.setOrderDetailDtoList(collect.get(orderDto.getOrderId()));
        }
        return orderDtoList;
    }


    @Override
    public List<OrderDto> getOutboundTaskByBillType(String billType, String warehouseArea, int isBindGrid) throws Exception {
        //eis组单调度开关
        String assemblyOrderSwitch = String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.ASSEMBLY_ORDER_SWITCH));

        List<OrderDto> orderDtoList = bizOutboundTaskService.getOutboundTaskByBillType(billType, warehouseArea, assemblyOrderSwitch, WmsConstants.check, isBindGrid);

        //过滤订单只有零发整的订单  不绑定格口
        /*List<BizOutboundTaskDetailDismantle> dismantleList = bizOutboundTaskDetailDismantleService.findByMap(MapUtils.put("status", 0).getMap());
        if(!CollectionUtils.isEmpty(dismantleList)){
            //有拆零的订单
            Set<Integer> temSet = dismantleList.stream().filter(o -> o.getType() == 2).map(BizOutboundTaskDetailDismantle::getOutboundId).collect(Collectors.toSet());
            Iterator<OrderDto> iterator = orderDtoList.iterator();
            while (iterator.hasNext()) {
                OrderDto orderDto = iterator.next();
                //判断订单是否有拆零
                if(!temSet.contains(orderDto.getOrderId())){
                    iterator.remove();
                }
            }
        }*/
        if (orderDtoList.isEmpty()) {
            return orderDtoList;
        }
        List<Integer> orderIds = orderDtoList.stream().map(OrderDto::getOrderId).collect(Collectors.toList());
        Map<Integer, List<OrderDetailDto>> map = getDetailByOrderIds(orderIds);
        for (OrderDto orderDto : orderDtoList) {
            List<OrderDetailDto> tempOrderDetailDtoList = map.get(orderDto.getOrderId());
            orderDto.setOrderDetailDtoList(tempOrderDetailDtoList);
        }
        return orderDtoList;
    }

    private Map<Integer, List<OrderDetailDto>> getDetailByOrderIds(List<Integer> orderIds) throws Exception {
        List<OrderDetailDto> orderDetailDtoList = bizOutboundTaskDetailService.getByOrderIds(orderIds);
        List<String> itemList = orderDetailDtoList.stream().map(OrderDetailDto::getItemId).distinct().collect(Collectors.toList());
        Map<String, ItemSyncVO> itemMap = feignGoodsService.queryItemMst(itemList).stream().collect(Collectors.toMap(ItemSyncVO::getItemId, o -> o));
        orderDetailDtoList.forEach(orderDetailDto -> {
            ItemSyncVO itemSyncVO = itemMap.get(orderDetailDto.getItemId());
            if (itemSyncVO != null) {
                orderDetailDto.setAbcType(itemSyncVO.getAbcType());
            }
        });
        return orderDetailDtoList.stream().collect(Collectors.groupingBy(OrderDetailDto::getOrderId));
    }

    public List<GroupGridDto> getGridList() {
        List<GroupGridDto> list = new ArrayList<>();
        List<GridInfoGroup> groupList = gridInfoGroupService.findByMap(MapUtils.put("isLock", BaseLockConstants.NO_LOCK).getMap());
        if (groupList.isEmpty()) {
            return list;
        }
        List<String> groupNos = groupList.stream().map(GridInfoGroup::getGroupNo).collect(Collectors.toList());
        groupList.forEach(option -> {
            GroupGridDto groupGridDto = new GroupGridDto();
            groupGridDto.setGroupId(option.getGroupNo());
            list.add(groupGridDto);
        });
        Criteria criteria = new Criteria(GridInfo.class);
        criteria.setRestriction(Restrictions.in("groupNo", groupNos.toArray())
                .and(Restrictions.eq("isLock", BaseLockConstants.NO_LOCK)));
        List<GridInfo> gridInfoList = gridInfoService.findByCriteria(criteria);

        Map<String, List<GridInfo>> map = gridInfoList.stream().collect(Collectors.groupingBy(GridInfo::getGroupNo));
        Iterator<GroupGridDto> iterator = list.iterator();
        while (iterator.hasNext()) {
            GroupGridDto dicGroupDto = iterator.next();
            List<GridInfo> dicSowingwallList = map.get(dicGroupDto.getGroupId());
            if (CollectionUtils.isEmpty(dicSowingwallList)) {
                iterator.remove();
                continue;
            }
            //找到组内空闲格口
            long usableGK = dicSowingwallList.stream()
                    .filter(s -> s.getOrderId() == null).count();
            dicGroupDto.setUsableGK(Integer.parseInt(usableGK + ""));
            //找到组内正在执行的任务
            long userWork = dicSowingwallList.stream()
                    .filter(s -> s.getOrderId() != null).count();
            dicGroupDto.setUserWork(Integer.parseInt(userWork + ""));
            if (dicGroupDto.getUsableGK() == 0) {
                iterator.remove();
            } else {
                dicGroupDto.setGridInfoList(dicSowingwallList);
            }
        }
        return list;
    }


    /**
     * 订单拆分格口
     *
     * @param orderDto 找格口逻辑   给格口绑定订单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doGridSplit(OrderDto orderDto) {

    }

}
