package com.wms.service.serviceImplements;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wms.constants.enums.StockStatus;
import com.wms.entity.table.Location;
import com.wms.entity.table.Stand;
import com.wms.entity.table.Stock;
import com.wms.entity.table.Task;
import com.wms.mapper.LocationMapper;
import com.wms.mapper.StandMapper;
import com.wms.mapper.StockMapper;
import com.wms.mapper.TaskMapper;
import com.wms.service.LocationService;
import com.wms.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

import static com.wms.constants.WmsConstants.MYSQL_JSON_CI;

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class LocationServiceImplements extends ServiceImpl<LocationMapper, Location> implements LocationService {
    /**
     * 库位
     */
    private final LocationMapper locationMapper;
    /**
     * 站台
     */
    private final StandMapper standMapper;
    /**
     * 库存
     */
    private final StockMapper stockMapper;
    /**
     * 任务
     */
    private final TaskMapper taskMapper;

    /**
     * 查找一个可用库位
     *
     * @param inPoint 入库站点
     * @param goodsId 物料编号--可选
     * @return 结果
     * nextLocationId 下一个可用库位
     */
    @Override
    public Map<String, String> getOneLocation(String inPoint, String goodsId) {
        Map<String, String> resultMap = new HashMap<>();
        // 添加对应设备的查询条件
        int equipmentId = -1;
        if (StringUtils.isNotEmpty(inPoint)) {
            // 查询到入参的站台所对应的设备
            LambdaQueryWrapper<Stand> standQueryWrapper = new LambdaQueryWrapper<Stand>()
                    .select(Stand::getStandId, Stand::getEquipmentId)
                    .eq(Stand::getStandId, inPoint);
            Stand queryStand = standMapper.selectOne(standQueryWrapper);
            if (queryStand != null && queryStand.getEquipmentId() != null) {
                equipmentId = queryStand.getEquipmentId();
            }
        }
        // 指定设备号
        if (equipmentId != -1) {
            return getOneLocationByEquipmentId(resultMap, equipmentId);
        } else {// 未指定设备号
            // 选择最近未使用的设备
            LambdaQueryWrapper<Stand> LRUStandQueryWrapper = new LambdaQueryWrapper<Stand>()
                    .select(Stand::getStandId, Stand::getEquipmentId)
                    .eq(Stand::getStandStatus, 0)
                    .eq(Stand::getIsLock, 0)
                    .eq(Stand::getStandType, 3)
                    .orderByAsc(Stand::getLastUseTime);
            List<Stand> LRUStands = standMapper.selectList(LRUStandQueryWrapper);
            if (!LRUStands.isEmpty()) {
                if (StringUtils.isNotEmpty(goodsId)) {// 需要根据物料编号做出区分
                    equipmentId = selectEquipmentByLRUAndGoods(LRUStands, goodsId);
                    if (equipmentId != -1) {
                        getOneLocationByEquipmentId(resultMap, equipmentId);
                    }
                }
                if (resultMap.isEmpty()) {// 结果集为空
                    for (Stand lruStand : LRUStands) {
                        getOneLocationByEquipmentId(resultMap, lruStand.getEquipmentId());
                        if (!resultMap.isEmpty()) {
                            // 更新最近使用时间
                            lruStand.setLastUseTime(LocalDateTime.now());
                            standMapper.updateById(lruStand);
                            break;
                        }
                    }
                }
            }
            return resultMap;
        }
    }

    /**
     * 根据设备号查找库位
     *
     * @param resultMap            结果集
     * @param equipmentId          设备号
     * @return 查询结果
     */
    private Map<String, String> getOneLocationByEquipmentId(Map<String, String> resultMap, int equipmentId) {
        resultMap.clear();
        LambdaQueryWrapper<Location> locationQueryWrapper = new LambdaQueryWrapper<Location>()
                .eq(Location::getAreaId, 1)
                .eq(Location::getLocationStatus, 0)
                .eq(Location::getLocationType, 1)
                .eq(Location::getIsLock, 0)
                .eq(Location::getEquipmentId, equipmentId)
                .orderByDesc(Location::getWDepth)
                .orderByAsc(List.of(Location::getWCol, Location::getWLayer, Location::getWRow));
        List<Location> availableLocations = locationMapper.selectList(locationQueryWrapper);
        for (Location oneAvailableLocation : availableLocations) {
            resultMap.put("nextLocationId", oneAvailableLocation.getLocationId());
            standMapper.update(new LambdaUpdateWrapper<Stand>()
                    .set(Stand::getLastUseTime, LocalDateTime.now())
                    .eq(Stand::getEquipmentId, equipmentId)
                    .eq(Stand::getStandType, 3));
            break;
        }
        return resultMap;
    }

    /**
     * 根据最近最久未使用加物料来判断应该使用哪个设备
     *
     * @param LRUStands 符合的设备
     * @param goodsId   物料编号
     * @return 设备号
     */
    private int selectEquipmentByLRUAndGoods(List<Stand> LRUStands, String goodsId) {
        int equipmentId = -1;
        List<Integer> equipmentIds = new ArrayList<>();
        List<Task> tasks = taskMapper.selectList(new LambdaQueryWrapper<Task>()
                .select(Task::getDestination)
                .apply("goods_related ->> '$.goodsId' = {0}" + MYSQL_JSON_CI, goodsId)
                .eq(Task::getTaskType, 1));
        for (Task task : tasks) {
            Location tempLocation = locationMapper.selectOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, task.getDestination()));
            if (tempLocation != null && !equipmentIds.contains(tempLocation.getEquipmentId())) {
                equipmentIds.add(tempLocation.getEquipmentId());
            }
        }
        List<Stock> stocks = stockMapper.selectList(new LambdaQueryWrapper<Stock>()
                .select(Stock::getLocationId)
                .apply("goods_related ->> '$.goodsId' = {0}" + MYSQL_JSON_CI, goodsId)
                .eq(Stock::getStockStatus, StockStatus.OK.getCode()));
        for (Stock stock : stocks) {
            Location tempLocation = locationMapper.selectOne(new LambdaQueryWrapper<Location>().eq(Location::getLocationId, stock.getLocationId()));
            if (tempLocation != null && !equipmentIds.contains(tempLocation.getEquipmentId())) {
                equipmentIds.add(tempLocation.getEquipmentId());
            }
        }
        for (Stand lruStand : LRUStands) {
            if (!equipmentIds.contains(lruStand.getEquipmentId())) {
                equipmentId = lruStand.getEquipmentId();
                break;
            }
        }
        return equipmentId;
    }
}
