package com.prolog.eis.strategy.outbound;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.prolog.eis.constants.base.AbcTypeConstant;
import com.prolog.eis.constants.base.ContainerTaskStatusConstants;
import com.prolog.eis.constants.base.OrderStatusConstants;
import com.prolog.eis.constants.sys.SysParamConstants;
import com.prolog.eis.dto.item.ItemSyncVO;
import com.prolog.eis.dto.outbound.HitNotificationDTO;
import com.prolog.eis.dto.outbound.HitNotificationReqDTO;
import com.prolog.eis.dto.outbound.HitNotificationResDTO;
import com.prolog.eis.dto.strategy.*;
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.outbound_dispatch.DispatchStrategy;
import com.prolog.eis.service.container.ContainerTaskService;
import com.prolog.eis.service.feign.FeignGoodsService;
import com.prolog.eis.service.outbound.BizOutboundTaskDetailService;
import com.prolog.eis.service.outbound.BizOutboundTaskService;
import com.prolog.eis.service.wms.WmsHitNotificationService;
import com.prolog.eis.store_init.StoreInitStrategy;
import com.prolog.eis.utils.GoodsPropertyUtils;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restrictions;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

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

import static com.prolog.eis.constants.base.BaseBillTypeConstants.REPLENISH_TYPE;


/**
 * 指定波次出库策略
 *
 * @author wk
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AssignWaveStrategy extends DispatchStrategy {

    private final BizOutboundTaskService bizOutboundTaskService;
    private final BizOutboundTaskDetailService bizOutboundTaskDetailService;
    private final ContainerTaskService containerTaskService;
    private final WmsHitNotificationService wmsHitNotificationService;
    private final FeignGoodsService feignGoodsService;
    private final RedisTemplate redisTemplate;

    @Override
    public void doDispatch(String billType, String warehouseArea, StoreInitStrategy storeInitStrategy) throws Exception {
        InitDto initDto = storeInitStrategy.getInitDto(billType, warehouseArea, this);
        if (Objects.isNull(initDto)) {
            return;
        }
        List<ContainerInfoDto> containerInfoDtoList = initDto.getContainerInfoDtoList();
        List<OrderDto> orderList = initDto.getOrderList();
        Map<Integer, String> strategyMap = initDto.getStrategyMap();
        orderList.sort(Comparator.comparing(OrderDto::getPriority, Comparator.nullsFirst(Integer::compare)).reversed().thenComparing(OrderDto::getCreateTime));
        Map<String, BigDecimal> storeMap =
                initDto.getContainerInfoDtoList().stream().collect(Collectors.toMap(ContainerInfoDto::getBusinessProperty, ContainerInfoDto::getQty, (k1, k2) -> k1.add(k2)));
        List<OrderDto> orderDtoList = new ArrayList<>();
        for (OrderDto orderDto : orderList) {
            if (computeStore(orderDto, storeMap)) {
                orderDtoList.add(orderDto);
            }
        }
        if (orderDtoList.isEmpty()) {
            throw new Exception("所有订单库存不足");
        }

        List<ContainerTaskDto> containerTaskDtoList = getGroupList(orderDtoList, containerInfoDtoList, strategyMap);
        handleOrder(containerTaskDtoList, null, null);
        containerTaskService.createContainerTaskBatch(containerTaskDtoList);
    }

    @Override
    public void handleOrder(List<ContainerTaskDto> containerTaskDtoList, String stationId, List<OrderDto> orderDtoList) throws Exception {
        System.out.println("指定波次订单处理");
        Map<Integer, List<ContainerTaskDto>> orderMap
                = containerTaskDtoList.stream().collect(Collectors.groupingBy(ContainerTaskDto::getOrderId));
        List<BizOutboundTaskDetail> updateBizOutboundTaskDetailList = new ArrayList<>();
        List<BizOutboundTask> updateBizOutboundTaskList = new ArrayList<>();
        for (Map.Entry<Integer, List<ContainerTaskDto>> orderContainerTask : orderMap.entrySet()) {
            Integer orderId = orderContainerTask.getKey();
            List<ContainerTaskDto> containerTaskDtos = orderContainerTask.getValue();
            List<BizOutboundTaskDetail> bizOutboundTaskDetailList = bizOutboundTaskDetailService.findByOrderId(orderId);
            BizOutboundTask bizOutboundTask = bizOutboundTaskService.findById(orderId);
            if (CompareUtil.compare(bizOutboundTask.getBillType(), REPLENISH_TYPE) == 0) {
                //超补订单处理
                handleOverSupplement(containerTaskDtoList, bizOutboundTask);
            }
            bizOutboundTaskDetailList.forEach(k -> {
                k.setStatus(OrderStatusConstants.START_STATUS);
                k.setUpdateTime(LocalDateTime.now());
            });
            updateBizOutboundTaskDetailList.addAll(bizOutboundTaskDetailList);
            bizOutboundTask.setStatus(OrderStatusConstants.START_STATUS);
            bizOutboundTask.setUpdateTime(LocalDateTime.now());
            updateBizOutboundTaskList.add(bizOutboundTask);
        }
        bizOutboundTaskDetailService.batchUpdate(updateBizOutboundTaskDetailList);
        bizOutboundTaskService.batchUpdate(updateBizOutboundTaskList);
    }

    /**
     * 增加超补逻辑
     */
    private void handleOverSupplement(List<ContainerTaskDto> containerTaskDtoList, BizOutboundTask bizOutboundTask) throws Exception {
        System.out.println("超补订单处理");
        List<HitNotificationDTO> list = new ArrayList<>();
        List<ContainerTaskDto> collect = containerTaskDtoList.stream().filter(x -> x.getOrderId().equals(bizOutboundTask.getId())).collect(Collectors.toList());

        List<ItemSyncVO> itemSyncVOS = feignGoodsService.queryItemMst(collect.stream().map(ContainerTaskDto::getItemId).distinct().collect(Collectors.toList()));

        for (ContainerTaskDto containerTaskDto : collect) {
            if (containerTaskDto.getAbcType().equals(AbcTypeConstant.TYPE_B) || containerTaskDto.getAbcType().equals(AbcTypeConstant.TYPE_C)) {
                //如果为BC品品，则需要判断是否满足超补条件
                Boolean superFill = containerSuperFill(containerTaskDto, itemSyncVOS);
                //如果不满足超补条件，则跳过
                if (!superFill) {
                    continue;
                }
            }
            HitNotificationDTO hitNotificationDTO = new HitNotificationDTO();
            hitNotificationDTO.setWorkType(bizOutboundTask.getWorkType());
            hitNotificationDTO.setBusinessType(bizOutboundTask.getBusinessType());
            hitNotificationDTO.setTaskId(bizOutboundTask.getUpperSystemTaskId());
            hitNotificationDTO.setBillNo(bizOutboundTask.getBillNo());
            hitNotificationDTO.setContainerNo(containerTaskDto.getContainerSubNo());
            hitNotificationDTO.setTaskLineId(containerTaskDto.getSeqNo());
            hitNotificationDTO.setOwnerId(containerTaskDto.getOwnerId());
            hitNotificationDTO.setItemId(containerTaskDto.getItemId());
            hitNotificationDTO.setLotId(containerTaskDto.getLotId());
            hitNotificationDTO.setPackId(containerTaskDto.getPackId());
            hitNotificationDTO.setWmsLocationCode(bizOutboundTask.getWmsLocationCode());
            hitNotificationDTO.setQty(containerTaskDto.getContainerInfoQyt().doubleValue());
            hitNotificationDTO.setLackQty(0.0);
            hitNotificationDTO.setExceedQyt(containerTaskDto.getContainerInfoQyt().subtract(containerTaskDto.getBindingNum()).doubleValue());
            list.add(hitNotificationDTO);
        }
        HitNotificationReqDTO hitNotificationReqDTO = new HitNotificationReqDTO();
        hitNotificationReqDTO.setSubList(list);
        hitNotificationReqDTO.setMsgId(UUID.randomUUID().toString());
        hitNotificationReqDTO.setWarehouseId(bizOutboundTask.getWmsWarehouseId());
        RestMessage<JSONObject> restMessage = wmsHitNotificationService.execute(hitNotificationReqDTO);
        if (!restMessage.isSuccess()) {
            log.error("超补订单【{}】处理失败：{}", collect, restMessage.getMessage());
            return;
        }
        HitNotificationResDTO hitNotificationResDTO = JSON.parseObject(restMessage.getData().toString(), HitNotificationResDTO.class);
        if (!Objects.isNull(hitNotificationResDTO) && CollectionUtil.isNotEmpty(hitNotificationResDTO.getExceedSubList())) {
            List<String> supercomplementList = hitNotificationResDTO.getExceedSubList().stream().map(HitNotificationDTO::getContainerNo).collect(Collectors.toList());
            log.warn("超补订单返回超补JSON【{}】", restMessage.getData());
            // 修改容器任务状态状态
            containerTaskDtoList.forEach(x -> {
                if (supercomplementList.contains(x.getContainerSubNo())) {
                    // 修改对象属性
                    x.setBindingNum(x.getContainerInfoQyt());
                    x.setContainerType(ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE);
                }
            });
        }
    }

    /**
     * BC品容器超补判断
     */
    private Boolean containerSuperFill(ContainerTaskDto containerTaskDto, List<ItemSyncVO> itemSyncVOS) {
        String bcSuperConversion = String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.BC_SUPER_CONVERSION));
        BigDecimal remainQty = containerTaskDto.getContainerInfoQyt().subtract(containerTaskDto.getBindingNum());
        ItemSyncVO itemSyncVO = itemSyncVOS.stream().filter(x -> x.getItemId().equals(containerTaskDto.getItemId())).findFirst().orElse(null);
        BigDecimal conversionRate = itemSyncVO.getUomList().get(0).getConversionRate();
        BigDecimal divide = remainQty.divide(conversionRate, 0, RoundingMode.HALF_UP);
        if (divide.compareTo(new BigDecimal(bcSuperConversion)) > 0) {
            return false;
        }
        return true;
    }

    @Override
    public void delUseContainer(List<ContainerInfoDto> collect, OrderDto orderDto) {
        Criteria criteria = new Criteria(ContainerTask.class);
        criteria.setRestriction(Restrictions.in("containerSubNo", collect.stream().map(ContainerInfoDto::getContainerSubNo).collect(Collectors.toList()).toArray()));
        List<ContainerTask> containerTasks = containerTaskService.findByCriteria(criteria);
        if (!CollectionUtils.isEmpty(containerTasks)) {
            Map<String, Long> orderNum = containerTasks.stream().collect(Collectors.groupingBy(ContainerTask::getContainerSubNo, Collectors.counting()));
            collect.forEach(o -> {
                if (orderNum.containsKey(o.getContainerSubNo())) {
                    o.setOrderNum(Integer.parseInt(orderNum.get(o.getContainerSubNo()) + ""));
                }
            });
            Set<String> set = containerTasks.stream().filter(o -> !orderDto.getBillType().equals(o.getTaskType())).map(ContainerTask::getContainerSubNo).distinct().collect(Collectors.toSet());
            if (set != null && set.size() > 0) {
                collect.remove(collect.stream().filter(o -> set.contains(o.getContainerSubNo())).findFirst().orElse(null));
            }
        }
    }


    private List<ContainerTaskDto> getGroupList(List<OrderDto> orderDtoList, List<ContainerInfoDto> containerInfoDtoList, Map<Integer, String> strategyMap) throws Exception {
        List<OrderDetailDto> orderDetailDtoList = getAllOrderDetailList(orderDtoList);
        Map<String, List<OrderDetailDto>> detailMap = orderDetailDtoList.stream().collect(Collectors.groupingBy(group -> GoodsPropertyUtils.getGoodsProperty(group.getItemId(), group.getLotId(), group.getOwnerId(), group.getPackId())));
        List<ContainerTaskDto> containerTaskDtoList = new ArrayList<>();
        for (String s : detailMap.keySet()) {
            List<OrderDetailDto> detailDtoList = detailMap.get(s);
            getAllTask(orderDtoList, containerInfoDtoList, strategyMap, containerTaskDtoList, detailDtoList);
        }
        return containerTaskDtoList;
    }


    private List<OrderDetailDto> getAllOrderDetailList(List<OrderDto> orderDtoList) {
        List<OrderDetailDto> orderDetailDtoList = new ArrayList<>();
        for (OrderDto orderDto : orderDtoList) {
            orderDetailDtoList.addAll(orderDto.getOrderDetailDtoList());
        }
        return orderDetailDtoList;
    }
}
