package cn.iocoder.yudao.module.order.service.orderWarehouseCheck;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.apollo.core.event.box.BoxOrderForceWarehouseCheckEvent;
import cn.iocoder.yudao.framework.apollo.core.event.box.BoxOrderRevokeWarehouseCheckEvent;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.depository.dal.dataobject.warehouse.WarehouseAreaDO;
import cn.iocoder.yudao.module.depository.dal.dataobject.warehouse.WarehouseAreaPositionDO;
import cn.iocoder.yudao.module.depository.dal.mysql.warehouse.WarehouseAreaMapper;
import cn.iocoder.yudao.module.depository.dal.mysql.warehouse.WarehouseAreaPositionMapper;
import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderItem.OrderItemDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderWarehouseCheck.OrderWarehouseCheckDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderWarehouseIn.OrderWarehouseInDO;
import cn.iocoder.yudao.module.order.dal.mysql.order.OrderMapper;
import cn.iocoder.yudao.module.order.dal.mysql.orderWarehouseCheck.OrderWarehouseCheckMapper;
import cn.iocoder.yudao.module.order.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.order.service.order.OrderBusinessService;
import cn.iocoder.yudao.module.order.service.order.OrderItemService;
import cn.iocoder.yudao.module.order.service.order.OrderLocationService;
import cn.iocoder.yudao.module.order.service.orderWarehouseIn.OrderWarehouseInService;
import cn.iocoder.yudao.module.order.vo.order.OrderBackPageVO;
import cn.iocoder.yudao.module.order.vo.order.OrderQueryVO;
import cn.iocoder.yudao.module.order.vo.order.WarehouseCheckInfoVO;
import cn.iocoder.yudao.module.order.vo.order.WarehouseInInfoVO;
import cn.iocoder.yudao.module.order.vo.orderItem.OrderItemCheckWarehouseVO;
import cn.iocoder.yudao.module.order.vo.orderLocation.OrderLocationBackVO;
import cn.iocoder.yudao.module.order.vo.orderLocation.OrderLocationMergeVO;
import cn.iocoder.yudao.module.order.vo.orderWarehouseCheck.CheckItemSumVO;
import cn.iocoder.yudao.module.order.vo.orderWarehouseCheck.OrderForceCheck;
import cn.iocoder.yudao.module.order.vo.orderWarehouseCheck.OrderWarehouseCheckUpdateItemVO;
import cn.iocoder.yudao.module.order.vo.orderWarehouseCheck.OrderWarehouseCheckUpdateReqVO;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

@Slf4j
@Service
@Validated
public class OrderWarehouseCheckServiceImpl
        extends AbstractService<OrderWarehouseCheckMapper, OrderWarehouseCheckDO>
        implements OrderWarehouseCheckService {
    @Autowired
    private OrderBusinessService orderBusinessService;

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private OrderWarehouseInService orderWarehouseInService;
    @Autowired
    private OrderItemService orderItemService;
    @Resource
    private OrderLocationService orderLocationService;

    @Resource
    private WarehouseAreaMapper warehouseAreaMapper;

    @Resource
    private WarehouseAreaPositionMapper warehouseAreaPositionMapper;

    @Override
    public List<OrderWarehouseCheckDO> list(long orderId) {
        return abstractMapper.selectList(OrderWarehouseCheckDO::getOrderId, orderId);
    }

    @Override
    public List<OrderWarehouseCheckDO> listByOrderItemId(long orderItemId) {
        return abstractMapper.selectList(OrderWarehouseCheckDO::getOrderItemId, orderItemId);
    }


    @Override
    public void check(long orderId) {
        Date now = DateUtil.date();
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        List<OrderWarehouseCheckDO> newWarehouseCheckDOList = new ArrayList<>();
        Map<Long, OrderItemCheckWarehouseVO> orderItemCheckWarehouseVOMap = new HashMap<>();
        List<OrderWarehouseCheckDO> warehouseCheckDOList = list(orderId);
        // 强制到仓信息一致
        if (CollectionUtil.isNotEmpty(warehouseCheckDOList)) {
            Map<Long, OrderWarehouseCheckDO> mappings = warehouseCheckDOList.stream().collect(Collectors.toMap(OrderWarehouseCheckDO::getWarehouseInId, Function.identity()));
            for (OrderWarehouseInDO inDo : orderWarehouseInService.selectList(OrderWarehouseInDO::getOrderId, orderId)) {
                OrderWarehouseCheckDO checkDo = mappings.get(inDo.getId());
                Long id = checkDo.getId();
                BeanUtils.copyProperties(inDo, checkDo);
                checkDo.setId(id);
                checkDo.setSource(3);
                checkDo.setUpdateTime(now);
                checkDo.setUpdater(String.valueOf(userId));
                abstractMapper.updateById(checkDo);
                newWarehouseCheckDOList.add(checkDo);
            }
            // 货物信息查询
            List<OrderItemDO> orderItemDOList = orderItemService.list(new LambdaQueryWrapper<OrderItemDO>().eq(OrderItemDO::getOrderId, orderId));
            // 货物的到仓信息分组
            Map<Long, List<OrderWarehouseCheckDO>> warehouseCheckDOListMap = newWarehouseCheckDOList.stream().collect(Collectors.groupingBy(OrderWarehouseCheckDO::getOrderItemId));
            for (OrderItemDO orderItemDO : orderItemDOList) {
                OrderItemCheckWarehouseVO orderItemCheckWarehouseVO = new OrderItemCheckWarehouseVO();
                BeanUtils.copyProperties(orderItemDO, orderItemCheckWarehouseVO);
                orderItemCheckWarehouseVO.setCheckBrand(orderItemDO.getBrand());
                orderItemCheckWarehouseVO.setCheckBrandType(orderItemDO.getFeeType());
                orderItemCheckWarehouseVO.setCheckMaterial(orderItemDO.getMaterial());
                orderItemCheckWarehouseVO.setCheckProdAttrIds(orderItemDO.getWarehouseInProdAttrIds());
                WarehouseCheckInfoVO warehouseCheckInfoVO = orderItemSetWarehouseCheckInfoVO(orderItemDO.getBoxGauge(), warehouseCheckDOListMap.get(orderItemDO.getOrderItemId()));
                orderItemCheckWarehouseVO.setWarehouseCheckInfoVO(warehouseCheckInfoVO);
                orderItemCheckWarehouseVOMap.put(orderItemDO.getOrderItemId(), orderItemCheckWarehouseVO);
            }
        } else {
            // 全部到仓，复制入仓信息
            for (OrderWarehouseInDO inDo : orderWarehouseInService.selectList(OrderWarehouseInDO::getOrderId, orderId)) {
                OrderWarehouseCheckDO checkDo = new OrderWarehouseCheckDO();
                BeanUtils.copyProperties(inDo, checkDo);
                checkDo.setId(null);
                checkDo.setWarehouseInId(inDo.getId());
                checkDo.setSource(3);
                checkDo.setCheckTime(now);
                checkDo.setCreateTime(now);
                checkDo.setCreator(String.valueOf(userId));
                checkDo.setUpdateTime(now);
                checkDo.setUpdater(String.valueOf(userId));
                abstractMapper.insert(checkDo);
                newWarehouseCheckDOList.add(checkDo);
            }
            // 货物信息查询
            List<OrderItemDO> orderItemDOList = orderItemService.list(new LambdaQueryWrapper<OrderItemDO>().eq(OrderItemDO::getOrderId, orderId));
            // 货物的到仓信息分组
            // 货物的到仓信息分组
            Map<Long, List<OrderWarehouseCheckDO>> warehouseCheckDOListMap = newWarehouseCheckDOList.stream().collect(Collectors.groupingBy(OrderWarehouseCheckDO::getOrderItemId));

            for (OrderItemDO orderItemDO : orderItemDOList) {
                OrderItemCheckWarehouseVO orderItemCheckWarehouseVO = new OrderItemCheckWarehouseVO();
                BeanUtils.copyProperties(orderItemDO, orderItemCheckWarehouseVO);
                orderItemCheckWarehouseVO.setCheckBrand(orderItemDO.getBrand());
                orderItemCheckWarehouseVO.setCheckBrandType(orderItemDO.getFeeType());
                orderItemCheckWarehouseVO.setCheckMaterial(orderItemDO.getMaterial());
                orderItemCheckWarehouseVO.setCheckProdAttrIds(orderItemDO.getWarehouseInProdAttrIds());
                WarehouseCheckInfoVO warehouseCheckInfoVO = orderItemSetWarehouseCheckInfoVO(orderItemDO.getBoxGauge(), warehouseCheckDOListMap.get(orderItemDO.getOrderItemId()));
                orderItemCheckWarehouseVO.setWarehouseCheckInfoVO(warehouseCheckInfoVO);
                orderItemCheckWarehouseVOMap.put(orderItemDO.getOrderItemId(), orderItemCheckWarehouseVO);
            }

        }
        // 合并单个货物信息的到仓总数据
        orderBusinessService.orderToWarehouse(orderId, orderItemCheckWarehouseVOMap);
    }

    @Override
    public void revoke(long orderId) {
        LambdaQueryWrapper<OrderWarehouseCheckDO> wrapper = Wrappers.lambdaQuery();

        // 清空到仓信息
        abstractMapper.delete(wrapper.eq(OrderWarehouseCheckDO::getOrderId, orderId));
        // 回退订单到仓状态及数据，并添加日志
        orderBusinessService.revokeToWarehouse(orderId);
        // 将未实际到仓的订单箱标号卸柜/到仓时间置null
        applicationContext.publishEvent(new BoxOrderRevokeWarehouseCheckEvent(orderId));
    }

    @Override
    public void force(OrderForceCheck orderForceCheck) {
        // 修改订单卸柜/到仓状态
        OrderDO orderDO = orderMapper.selectById(orderForceCheck.getOrderId());
        if (Objects.isNull(orderDO)) {
            throw exception(ErrorCodeConstants.ORDER_NOT_EXISTS);
        }
        applicationContext.publishEvent(new BoxOrderForceWarehouseCheckEvent(orderDO.getOrderId(), orderDO.getContainerNumber(), orderForceCheck.getUnloadTime(), orderDO.getTransportId()));
        // 同步到仓数据
        check(orderForceCheck.getOrderId());
    }

    @Override
    public void allForce() {
        OrderQueryVO queryVO = new OrderQueryVO();
        queryVO.setInWarehouseState(218);
        long allCount = orderMapper.orderCount(queryVO);
        int size = 1000;
        int start = 0;
        while (start <= allCount) {
            List<OrderBackPageVO> list = orderMapper.orderList(start, size, queryVO);
            if(CollectionUtil.isNotEmpty(list)) {
                list.stream().map(OrderBackPageVO::getOrderId).forEach(this::check);
            }
            start += size;
        }
    }

    @Override
    public void update(OrderWarehouseCheckUpdateReqVO checkUpdateReqVO) {
        Map<Long, OrderWarehouseCheckDO> mappings = listByOrderItemId(checkUpdateReqVO.getOrderItemId()).stream()
                .collect(Collectors.toMap(OrderWarehouseCheckDO::getId, Function.identity()));
        ServiceException exception = exception(ErrorCodeConstants.ORDER_WAREHOUSE_CHECK_NOT_EXISTS);
        List<OrderWarehouseCheckDO> newWarehouseCheckDOList = new ArrayList<>();
        Date now = DateUtil.date();
        Long userId = SecurityFrameworkUtils.getLoginUserId();
        // 应用更新数据到到仓详情
        for (OrderWarehouseCheckUpdateItemVO update : checkUpdateReqVO.getUpdates()) {
            OrderWarehouseCheckDO checkDo = Optional.ofNullable(mappings.get(update.getId()))
                    .orElseThrow(() -> exception);
            BeanUtils.copyProperties(update, checkDo);
            checkDo.setSource(3);
            checkDo.setUpdateTime(now);
            checkDo.setUpdater(String.valueOf(userId));
            newWarehouseCheckDOList.add(checkDo);
        }

        // 统计并更新到数据库
        abstractMapper.updateBatch(newWarehouseCheckDOList);
        // 更新到订单中汇总
        // 货物信息查询
        OrderItemDO orderItemDO = orderItemService.getById(checkUpdateReqVO.getOrderItemId());
        // 货物的到仓信息分组
        Map<Long, OrderItemCheckWarehouseVO> orderItemCheckWarehouseVOMap = new HashMap<>();
        OrderItemCheckWarehouseVO orderItemCheckWarehouseVO = new OrderItemCheckWarehouseVO();
        BeanUtils.copyProperties(orderItemDO, orderItemCheckWarehouseVO);
        if (Objects.nonNull(checkUpdateReqVO.getProdId())) {
            orderItemCheckWarehouseVO.setProdId(checkUpdateReqVO.getProdId());
        }
        if (Objects.nonNull(checkUpdateReqVO.getProdType())) {
            orderItemCheckWarehouseVO.setProdType(checkUpdateReqVO.getProdType());
        }
        if (StringUtils.isNotBlank(checkUpdateReqVO.getProdTitleZh())) {
            orderItemCheckWarehouseVO.setProdTitleZh(checkUpdateReqVO.getProdTitleZh());
        }
        if (StringUtils.isNotBlank(checkUpdateReqVO.getProdTitleZh())) {
            orderItemCheckWarehouseVO.setProdTitleEn(checkUpdateReqVO.getProdTitleEn());
        }
        orderItemCheckWarehouseVO.setCheckBrand(orderItemDO.getBrand());
        orderItemCheckWarehouseVO.setCheckBrandType(orderItemDO.getFeeType());
        orderItemCheckWarehouseVO.setCheckMaterial(orderItemDO.getMaterial());
        orderItemCheckWarehouseVO.setCheckProdAttrIds(orderItemDO.getWarehouseInProdAttrIds());
        WarehouseCheckInfoVO warehouseCheckInfoVO = orderItemSetWarehouseCheckInfoVO(orderItemDO.getBoxGauge(), newWarehouseCheckDOList);
        orderItemCheckWarehouseVO.setWarehouseCheckInfoVO(warehouseCheckInfoVO);
        orderItemCheckWarehouseVOMap.put(orderItemDO.getOrderItemId(), orderItemCheckWarehouseVO);
        orderBusinessService.orderToWarehouse(checkUpdateReqVO.getOrderId(), orderItemCheckWarehouseVOMap);
    }

    private WarehouseCheckInfoVO orderItemSetWarehouseCheckInfoVO(String orderItemBoxGauge, List<OrderWarehouseCheckDO> orderWarehouseCheckDOListExist) {
        // 订单箱数 可判断多箱少箱异常

        Integer quantitySum = 0;
        BigDecimal weightSum = BigDecimal.ZERO;
        BigDecimal volumeSum = BigDecimal.ZERO;
        List<String> expressNoList = new ArrayList<>();
        List<String> remarkList = new ArrayList<>();

        Date checkTime = null;
        String unit = null;
        String units = null;
        Integer specificationType = null;
        String boxGauge = null;
        Boolean isMultiSpecification = false;
        Integer mixStatus = 0;

        int cartonsNumSum = 0;
        if (CollectionUtil.isNotEmpty(orderWarehouseCheckDOListExist)) {

            isMultiSpecification = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getSpecificationType).filter(Objects::nonNull).distinct().count() > 1;
            cartonsNumSum = orderWarehouseCheckDOListExist.stream().filter(t -> t.getCartonsNum() != null).mapToInt(OrderWarehouseCheckDO::getCartonsNum).sum();
            quantitySum = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getQuantityAll).filter(Objects::nonNull).reduce(Integer::sum).orElse(0);
            weightSum = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getWeight).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            volumeSum = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getVolume).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
            expressNoList = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getExpressNo).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
            remarkList = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getRemark).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
            unit = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getUnit).filter(Objects::nonNull).sorted().findFirst().orElse(null);
            specificationType = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getSpecificationType).filter(Objects::nonNull).sorted().findFirst().orElse(null);
            units = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getUnit).filter(Objects::nonNull).sorted().distinct().collect(Collectors.joining(","));
            checkTime = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getCheckTime).filter(Objects::nonNull).sorted().findFirst().orElse(null);
            boxGauge = orderWarehouseCheckDOListExist.stream().map(OrderWarehouseCheckDO::getBoxGauge).filter(Objects::nonNull).sorted().findFirst().orElse(null);
            if (StringUtils.isBlank(boxGauge)) {
                throw exception(ErrorCodeConstants.BOX_GAUGE_FORMAT_ERROR);
            }
            String[] gauge = boxGauge.split("\\*");
            if (gauge.length != 3) {
                throw exception(ErrorCodeConstants.BOX_GAUGE_FORMAT_ERROR);
            }
            mixStatus = orderWarehouseCheckDOListExist.stream().anyMatch(t -> CollectionUtil.isNotEmpty(t.getOrderWarehouseInDetailsVOList())) ? 1 : 0;
        }


        // WarehouseCheckInfoVO
        WarehouseCheckInfoVO warehouseCheckInfoVO = new WarehouseCheckInfoVO();
        warehouseCheckInfoVO.setIsMultiSpecification(isMultiSpecification);
        warehouseCheckInfoVO.setCartonsNum(cartonsNumSum);
        warehouseCheckInfoVO.setQuantity(quantitySum);
        warehouseCheckInfoVO.setUnit(unit);
        warehouseCheckInfoVO.setSpecificationType(specificationType);
        warehouseCheckInfoVO.setUnits(units);
        warehouseCheckInfoVO.setBoxGauge(orderItemBoxGauge);
        warehouseCheckInfoVO.setVolume(volumeSum);
        warehouseCheckInfoVO.setWeight(weightSum);
        warehouseCheckInfoVO.setBoxGauge(boxGauge);
        warehouseCheckInfoVO.setExpressNo(CollectionUtil.isNotEmpty(expressNoList) ? String.join(",", expressNoList) : "");
        warehouseCheckInfoVO.setRemark(CollectionUtil.isNotEmpty(remarkList) ? String.join(",", remarkList) : "");
        warehouseCheckInfoVO.setMixStatus(mixStatus);
        warehouseCheckInfoVO.setCheckTime(checkTime);

        // TODO 暂时不支持设置储位

//        List<OrderLocationBackVO> orderLocationBackVOList = orderLocationService.getOrderLocationBackVO(orderId, orderWarehouseCheckDOListExist);
//        if (CollectionUtil.isNotEmpty(orderLocationBackVOList)) {
//            warehouseCheckInfoVO.setOrderLocationBackVOList(orderLocationBackVOList);
//
//            Map<Long, WarehouseAreaDO> wareMap = warehouseAreaMapper.selectList().stream().collect(Collectors.toMap(WarehouseAreaDO::getId, v -> v));
//            Map<Long, WarehouseAreaPositionDO> positionMap = warehouseAreaPositionMapper.selectList().stream().collect(Collectors.toMap(WarehouseAreaPositionDO::getId, v -> v));
//
//            List<OrderLocationMergeVO> orderLocationMergeVOSet = null;
//            // 合并 并设置名称
//            if (CollectionUtil.isNotEmpty(orderLocationBackVOList)) {
//                orderLocationMergeVOSet = new ArrayList<>();
//                List<OrderLocationMergeVO> mergeList = orderLocationBackVOList.stream().map(orderLocationBackVO ->
//                        new OrderLocationMergeVO(orderLocationBackVO.getWareId(), orderLocationBackVO.getAreaId(), orderLocationBackVO.getLocationId())).collect(Collectors.toList());
//
//                for (OrderLocationMergeVO orderLocation : mergeList) {
//                    // 过滤
//                    if (orderLocationMergeVOSet.stream().noneMatch(t -> t.equals(orderLocation))) {
//
//                        this.setLocationName(orderLocation, wareMap, positionMap);
//
//                        orderLocationMergeVOSet.add(orderLocation);
//                    }
//                }
//            }
//
//            warehouseCheckInfoVO.setOrderLocationMergeVOSet(orderLocationMergeVOSet);
//        }

        return warehouseCheckInfoVO;
    }


    private void setLocationName(OrderLocationMergeVO orderLocation, Map<Long, WarehouseAreaDO> wareMap, Map<Long, WarehouseAreaPositionDO> positionMap) {
        String wareName = "";
        if (orderLocation.getWareId() != null) {
            WarehouseAreaDO wareDO = wareMap.get(orderLocation.getWareId());
            if (wareDO != null) {
                wareName = wareDO.getName();
            }
        }
        orderLocation.setWareName(wareName);

        String areaName = "";
        if (orderLocation.getAreaId() != null) {
            WarehouseAreaDO areaDO = wareMap.get(orderLocation.getAreaId());
            if (areaDO != null) {
                areaName = areaDO.getName();
            }
        }
        orderLocation.setAreaName(areaName);

        String positionName = "";
        if (orderLocation.getLocationId() != null) {
            WarehouseAreaPositionDO positionDO = positionMap.get(orderLocation.getLocationId());
            if (positionDO != null) {
                positionName = positionDO.getCode();
            }
        }
        orderLocation.setLocationName(positionName);
    }
}
