package com.prolog.eis.outbound_dispatch;

import cn.hutool.core.collection.CollectionUtil;
import com.prolog.eis.api.StrategyApi;
import com.prolog.eis.constants.base.ContainerTaskStatusConstants;
import com.prolog.eis.dto.strategy.ContainerInfoDto;
import com.prolog.eis.dto.strategy.ContainerTaskDto;
import com.prolog.eis.dto.strategy.OrderDetailDto;
import com.prolog.eis.dto.strategy.OrderDto;
import com.prolog.eis.store_init.StoreInitStrategy;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.utils.AssertUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

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

@Slf4j
public abstract class DispatchStrategy {


    public abstract void doDispatch(String billType, String warehouseArea, StoreInitStrategy storeInitStrategy) throws Exception;

    public boolean computeStore(OrderDto orderDto, Map<String, BigDecimal> storeMap) {
        Map<String, BigDecimal> tempMap = storeMap;
        List<OrderDetailDto> orderDetailDtoList = orderDto.getOrderDetailDtoList();
        if (CollectionUtils.isEmpty(orderDetailDtoList)) {
            log.warn("订单id【{}】下无对应订单明细", orderDto.getOrderId());
            return false;
        }
        for (OrderDetailDto orderDetailDto : orderDetailDtoList) {
            BigDecimal qty = tempMap.get(orderDetailDto.getBusinessProperty());
            if (Objects.isNull(qty)) {
                log.warn("订单明细【{}】所需的品种【{}】库存数【{}】不足", orderDetailDto.getOrderDetailId(), orderDetailDto.getBusinessProperty(), orderDetailDto.getPlanNum(), qty);
                return false;
            }
            if (qty.compareTo(orderDetailDto.getPlanNum()) < 0) {
                log.warn("订单明细【{}】所需的品种【{}】所需数量【{}】库存数【{}】不足", orderDetailDto.getOrderDetailId(), orderDetailDto.getBusinessProperty(), orderDetailDto.getPlanNum(), qty);
                return false;
            } else if (qty.equals(orderDetailDto.getPlanNum())) {
                tempMap.remove(orderDetailDto.getBizPropertyId());
            } else {
                tempMap.put(orderDetailDto.getBusinessProperty(), qty.subtract(orderDetailDto.getPlanNum()));
            }
        }
        storeMap = tempMap;
        System.out.println(storeMap);
        return true;
    }

    /**
     * 通过业务属性对比库存
     *
     * @param orderDto             订单
     * @param containerInfoDtoList 库存集合
     * @return
     */
    public boolean computeStoreByBusinessProperty(OrderDto orderDto, List<ContainerInfoDto> containerInfoDtoList) {
        List<OrderDetailDto> orderDetailDtoList = orderDto.getOrderDetailDtoList();
        for (OrderDetailDto orderDetailDto : orderDetailDtoList) {
            log.debug("订单明细【{}】包含商品ID【{}】批次ID【{}】货主ID【{}】包装ID【{}】", orderDetailDto.getOrderDetailId(), orderDetailDto.getItemId(), orderDetailDto.getLotId(), orderDetailDto.getOwnerId(), orderDetailDto.getPackId());
            List<ContainerInfoDto> collect = containerInfoDtoList.stream().filter(x -> compareBusinessProperty(orderDetailDto, x)).collect(Collectors.toList());
            if (CollectionUtil.isEmpty(collect)) {
//                log.warn("订单明细【{}】所需的品种【{}】库存数【{}】不足", orderDetailDto.getOrderDetailId(), orderDetailDto.getBizPropertyId(), orderDetailDto.getPlanNum(), qty);
                return false;
            }
            BigDecimal qty = collect.stream().map(ContainerInfoDto::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal needQty = orderDetailDto.getPlanNum();
            while (needQty.compareTo(BigDecimal.ZERO) > 0) {
                if (qty.compareTo(orderDetailDto.getPlanNum()) < 0) {
                    log.warn("订单明细【{}】所需的品种【{}】所需数量【{}】库存数【{}】不足", orderDetailDto.getOrderDetailId(), orderDetailDto.getBusinessProperty(), orderDetailDto.getPlanNum(), qty);
                    return false;
                } else if (qty.compareTo(orderDetailDto.getPlanNum()) == 0) {
                    containerInfoDtoList.removeAll(collect);
                } else {
                    needQty = deductQty(containerInfoDtoList, orderDetailDto.getPlanNum(), collect);
                }
            }
        }
        return true;
    }

    /**
     * 扣减数量
     *
     * @param containerInfoDtoList
     * @param qty
     * @param collect
     */
    private BigDecimal deductQty(List<ContainerInfoDto> containerInfoDtoList, BigDecimal qty, List<ContainerInfoDto> collect) {
        for (ContainerInfoDto containerInfoDto : collect) {
            if (qty.compareTo(BigDecimal.ZERO) <= 0) {
                return BigDecimal.ZERO;
            }
            BigDecimal subtractQty = qty.subtract(containerInfoDto.getQty());
            if (subtractQty.compareTo(BigDecimal.ZERO) < 0) {
                containerInfoDto.setQty(BigDecimal.ZERO.subtract(subtractQty));
                return BigDecimal.ZERO;
            } else {
                containerInfoDtoList.remove(containerInfoDto);
                qty = subtractQty;
            }
        }
        return BigDecimal.ZERO;
    }

    /**
     * 校验业务属性
     * -- 注意：如果库存没有品批这里可能会null指针
     * -- 解决：入库要求传业务属性
     *
     * @param orderDetailDto
     * @param containerInfoDto
     * @return
     */
    protected boolean compareBusinessProperty(OrderDetailDto orderDetailDto, ContainerInfoDto containerInfoDto) {
        boolean lotId = true;
        boolean itemId = true;
        boolean ownerId = true;
        boolean packId = true;
        if (Objects.nonNull(orderDetailDto.getLotId())) {
            lotId = orderDetailDto.getLotId().equals(containerInfoDto.getLotId());
        }
        if (Objects.nonNull(orderDetailDto.getItemId())) {
            itemId = orderDetailDto.getItemId().equals(containerInfoDto.getItemId());
        }
        if (Objects.nonNull(orderDetailDto.getOwnerId())) {
            ownerId = orderDetailDto.getOwnerId().equals(containerInfoDto.getOwnerId());
        }
        if (Objects.nonNull(orderDetailDto.getPackId())) {
            packId = orderDetailDto.getPackId().equals(containerInfoDto.getPackId());
        }
        return lotId && itemId && ownerId && packId;
    }

    public abstract void handleOrder(List<ContainerTaskDto> containerTaskDtoList, String stationId, List<OrderDto> orderDtoList) throws Exception;


    public static ContainerInfoDto getContainerInfoDto(List<ContainerInfoDto> containerInfoDtoList, List<ContainerInfoDto> bestContainer) {
        return containerInfoDtoList.stream().filter(x -> x.getContainerSubNo().equals(bestContainer.get(0).getContainerSubNo()) && x.getBusinessProperty().equals(bestContainer.get(0).getBusinessProperty())).findFirst().get();
    }

    public static ContainerInfoDto getContainerInfoDtoBybusiness(List<ContainerInfoDto> containerInfoDtoList, List<ContainerInfoDto> bestContainer) {
        return containerInfoDtoList.stream().filter(x -> x.getContainerSubNo().equals(bestContainer.get(0).getContainerSubNo()) && x.getBusinessProperty().equals(bestContainer.get(0).getBusinessProperty())).findFirst().get();
    }

    public ContainerTaskDto computeTask(OrderDto orderDto, OrderDetailDto orderDetailDto, ContainerInfoDto containerInfoDto, BigDecimal qty, int type) {
        ContainerTaskDto containerTaskDto = new ContainerTaskDto();
        containerTaskDto.setOrderId(orderDetailDto.getOrderId());
        containerTaskDto.setOrderDetailId(orderDetailDto.getOrderDetailId());
        containerTaskDto.setContainerType(type);
        containerTaskDto.setBizPropertyId(containerInfoDto.getBizPropertyId());
        containerTaskDto.setContainerSubNo(containerInfoDto.getContainerSubNo());
        containerTaskDto.setContainerNo(containerInfoDto.getContainerNo());
        containerTaskDto.setTaskType(orderDto.getBillType());
        containerTaskDto.setCompanyId(orderDto.getCompanyId());
        containerTaskDto.setWaveNo(orderDto.getWaveNo());
        containerTaskDto.setTargetArea(orderDetailDto.getTargetArea());
        containerTaskDto.setPriority(orderDto.getPriority());
        containerTaskDto.setWmsLocationCode(orderDto.getWmsLocationCode());
        containerTaskDto.setItemId(containerInfoDto.getItemId());
        containerTaskDto.setLotId(containerInfoDto.getLotId());
        containerTaskDto.setOwnerId(containerInfoDto.getOwnerId());
        containerTaskDto.setPackId(containerInfoDto.getPackId());
        containerTaskDto.setSeqNo(orderDetailDto.getSeqNo());
        containerTaskDto.setContainerInfoQyt(containerInfoDto.getQty());
        containerTaskDto.setAbcType(orderDetailDto.getAbcType());
        switch (type) {
            case ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE:
                containerTaskDto.setBindingNum(containerInfoDto.getQty());
                break;
            case ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE:
                containerTaskDto.setBindingNum(containerInfoDto.getQty());
                break;
            case ContainerTaskStatusConstants.HALF_TRAY_TYPE:
                containerTaskDto.setBindingNum(qty);
                break;
            default:
        }
        return containerTaskDto;
    }

    public void getAllTask(List<OrderDto> orderDtos, List<ContainerInfoDto> containerInfoDtoList, Map<Integer, String> strategyMap, List<ContainerTaskDto> containerTaskDtoList, List<OrderDetailDto> detailDtoList) throws Exception {

        for (OrderDetailDto orderDetailDto : detailDtoList) {
            List<ContainerInfoDto> collect = containerInfoDtoList.stream().filter(x -> Objects.equals(x.getBusinessProperty(), orderDetailDto.getBusinessProperty()) && x.getQty().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
            OrderDto orderDto = orderDtos.stream().filter(x -> x.getOrderId().equals(orderDetailDto.getOrderId())).findFirst().orElseThrow(() -> new PrologException("集合中没有找到订单id：" + orderDetailDto.getOrderId()));
            BigDecimal qty = orderDetailDto.getPlanNum();

            while (qty.compareTo(BigDecimal.ZERO) > 0) {
                ContainerTaskDto containerTaskDto = null;
                //剔除不可追加的托盘，（不同波次、不同订单类型）
                delUseContainer(collect, orderDto);
                AssertUtils.isNotTrue(CollectionUtil.isEmpty(collect), "无可用库存");
                //获取最佳托盘
                List<ContainerInfoDto> bestContainer = StrategyApi.getBestContainer(strategyMap, collect, qty);
                AssertUtils.isNotTrue(CollectionUtils.isEmpty(bestContainer), "订单ID：[%s]，订单详情ID:[%s],可出托盘正在任务中，不可追加任务");
                ContainerInfoDto containerInfoDto = getContainerInfoDto(containerInfoDtoList, bestContainer);
                //整托（托盘有锁定数量的时候不可整托出库）
                if (qty.compareTo(containerInfoDto.getQty()) >= 0 && containerInfoDto.getLockQty().compareTo(BigDecimal.ZERO) == 0) {
                    //查有无在途追加的托盘追加为整托的情况
                    Long count = containerTaskDtoList.stream().filter(o -> o.getContainerSubNo().equals(containerInfoDto.getContainerSubNo())).count();
                    int type = ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE;
                    if (containerInfoDto.getOrderNum() > 0 || count > 0) {
                        type = ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE;
                    }
                    containerTaskDto = computeTask(orderDto, orderDetailDto, containerInfoDto, qty, type);
                    qty = qty.subtract(containerInfoDto.getQty());
                    containerInfoDto.setQty(BigDecimal.ZERO);
                    collect.remove(bestContainer.get(0));
                } else {
//                    if (orderDto.getBillType() == BaseBillTypeConstants.REPLENISH_TYPE && AbcTypeConstant.TYPE_A.equals(orderDetailDto.getAbcType())) {
//                        containerTaskDto = computeTask(orderDto, orderDetailDto, containerInfoDto, qty, ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE);
//                        containerInfoDto.setQty(BigDecimal.ZERO);
//                        collect.remove(bestContainer.get(0));
//                    } else {
                    //拼托
                    containerTaskDto = computeTask(orderDto, orderDetailDto, containerInfoDto, qty, ContainerTaskStatusConstants.HALF_TRAY_TYPE);
                    containerInfoDto.setQty(containerInfoDto.getQty().subtract(qty));
//                    }
                    qty = BigDecimal.ZERO;
                }
                containerTaskDtoList.add(containerTaskDto);
                computerStoreTaskNum(collect, bestContainer.get(0));

            }

        }
    }

    public abstract void delUseContainer(List<ContainerInfoDto> collect, OrderDto orderDto);


    public void computerStoreTaskNum(List<ContainerInfoDto> containerTaskDtoList, ContainerInfoDto bestContainer) {
        containerTaskDtoList.stream().filter(x -> Objects.equals(x.getAreaNo(), bestContainer.getAreaNo())).forEach(x -> x.setTaskNum(x.getTaskNum() + 1));
    }


}
