package cn.iocoder.yudao.module.system.service.robot.job;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.mqtt.api.task.RobotTaskApi;
import cn.iocoder.yudao.module.system.config.poperties.ZnConfigConstant;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMovePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMoveSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseLocationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskAutoMoveDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDetailDO;
import cn.iocoder.yudao.module.system.dal.mysql.houselocation.WareHouseLocationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.positionmap.PositionMapItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskAutoMoveMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskDetailMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskMapper;
import cn.iocoder.yudao.module.system.enums.common.ZeroOneEnum;
import cn.iocoder.yudao.module.system.enums.robot.*;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

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

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;


import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.TASK_AUTO_MOVE_NOT_EXISTS;

/**
 * 机器人自动移库 Service 实现类
 *
 * @author 陈宾顺
 */
@Service
@Validated
@Slf4j
public class RobotTaskAutoMoveServiceImpl implements RobotTaskAutoMoveService {

    @Resource
    private RobotTaskAutoMoveMapper taskAutoMoveMapper;

    @Autowired
    private RobotTaskDetailMapper robotTaskDetailMapper;

    @Resource
    private WareHouseLocationMapper locationMapper;

    @Autowired
    private ZnConfigConstant znConfigConstant;

    @Override
    public Long createTaskAutoMove(RobotTaskAutoMoveSaveReqVO createReqVO) {
        // 插入
        RobotTaskAutoMoveDO taskAutoMove = BeanUtils.toBean(createReqVO, RobotTaskAutoMoveDO.class);
        taskAutoMoveMapper.insert(taskAutoMove);
        // 返回
        return taskAutoMove.getId();
    }

    @Override
    public void updateTaskAutoMove(RobotTaskAutoMoveSaveReqVO updateReqVO) {
        // 校验存在
        validateTaskAutoMoveExists(updateReqVO.getId());
        // 更新
        RobotTaskAutoMoveDO updateObj = BeanUtils.toBean(updateReqVO, RobotTaskAutoMoveDO.class);
        taskAutoMoveMapper.updateById(updateObj);
    }

    @Override
    public void deleteTaskAutoMove(Long id) {
        // 校验存在
        validateTaskAutoMoveExists(id);
        // 删除
        taskAutoMoveMapper.deleteById(id);
    }

    private void validateTaskAutoMoveExists(Long id) {
        if (taskAutoMoveMapper.selectById(id) == null) {
            throw exception(TASK_AUTO_MOVE_NOT_EXISTS);
        }
    }

    @Override
    public RobotTaskAutoMoveDO getTaskAutoMove(Long id) {
        return taskAutoMoveMapper.selectById(id);
    }

    @Override
    public PageResult<RobotTaskAutoMoveDO> getTaskAutoMovePage(RobotTaskAutoMovePageReqVO pageReqVO) {
        return taskAutoMoveMapper.selectPage(pageReqVO);
    }

    /**
     * 自动移库
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void distributeAutoMoveJob() {
        /*TenantContextHolder.setTenantId(1L);

        //todo 有循环的任务，是否要关闭自动移库的功能.做成配置
        if (!cycleDoAutoMove) {
            RobotTaskDO robotTaskDO = robotTaskMapper.selectOne(new LambdaQueryWrapperX<RobotTaskDO>()
                    .in(RobotTaskDO::getTaskStatus, RobotTaskStatusEnum.NEW.getType(), RobotTaskStatusEnum.DOING.getType())
                    .eq(RobotTaskDO::getDoCycle, ZeroOneEnum.ONE.getType()));
            if (ObjectUtil.isNotEmpty(robotTaskDO)) {
                log.info("存在循环的任务,不开启自动移库功能 :{}",robotTaskDO.getTaskNo());
                return;
            }
        }

        Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> robotAndTaskDetails =
                distributeTasksService.getRobotAndTaskDetails();

        List<RobotInformationDO> robots = robotAndTaskDetails.getLeft();
        if (ObjectUtil.isEmpty(robots)) {
            log.info("没有空闲的机器人能执行移库任务");
            return;
        }

        List<RobotTaskDetailDO> taskDetailDOS = robotAndTaskDetails.getRight();
        if (ObjectUtil.isEmpty(taskDetailDOS)) {
            log.info("没有需要自动移库的明细任务");
            return;
        }

        //查询需要移库的普通库位(非线库)
        List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> ordinaryPairs = getOrdinaryAutoMoveLocation(taskDetailDOS);

        //查询需要移库的库位(线库)
        List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> pairs = getLaneAutoMoveLocation(taskDetailDOS);

        if (ObjectUtil.isEmpty(ordinaryPairs) && ObjectUtil.isEmpty(pairs)) {
            log.info("查不到需要移库的库位");
            return;
        }
        ordinaryPairs.addAll(pairs);
        //下发任务
        List<RobotTaskAutoMoveDO> robotTaskAutoMoveDOS = allocationRobot(robots, ordinaryPairs);
        if (ObjectUtil.isEmpty(robotTaskAutoMoveDOS)) {
            log.info("暂无需要下发移库的任务");
            return;
        }

        taskAutoMoveMapper.insert(robotTaskAutoMoveDOS);
        Set<String> robotNos = robotTaskAutoMoveDOS.stream().map(RobotTaskAutoMoveDO::getRobotNo).collect(Collectors.toSet());

        List<RobotInformationDO> robotInformationDOS = robotInformationMapper.selectByRobotNos(robotNos);
        robotInformationDOS.stream().forEach(robotInformationDO -> {
            robotInformationDO.setRobotStatus(RobotStatusEnum.DOING.getType());
        });
        robotInformationMapper.updateById(robotInformationDOS);

        List<Long> locationIds = new ArrayList<>();
        robotTaskAutoMoveDOS.forEach(robotTaskAutoMoveDO -> {
            locationIds.add(robotTaskAutoMoveDO.getFromLocationId());
            locationIds.add(robotTaskAutoMoveDO.getToLocationId());
        });

        List<List<Long>> fromPartition = Lists.partition(locationIds, 100);
        fromPartition.forEach(list-> {
//            locationMapper.updateLocationLockList(list,0l,LocationLockEnum.NO.getType());
        });*/

        // todo 搬运任务下发给机器人

    }

    /**
     * 查询需要移库的普通库位(非线库)
     * @param taskDetailDOS
     * @return
     */
    private List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> getOrdinaryAutoMoveLocation(List<RobotTaskDetailDO> taskDetailDOS) {
        List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> result = new ArrayList<>();
        List<RobotTaskDetailDO> details = taskDetailDOS.stream().filter(v ->
                        (RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(v.getTaskType())
                                || RobotTaskTypeEnum.TAKE.getType().equals(v.getTaskType())
                                || RobotTaskTypeEnum.RELEASE.getType().equals(v.getTaskType()))
                                && ObjectUtil.isEmpty(v.getFromLaneId()))
                .collect(Collectors.toList());
        if (ObjectUtil.isEmpty(details)) {
            log.info("普通库位, 不需要执行移库");
            return result;
        }

        Set<Long> doingLocationIds = new HashSet<>();
        for (RobotTaskDetailDO robotTaskDetailDO : details) {
            if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLocationId())) {
                doingLocationIds.add(robotTaskDetailDO.getFromLocationId());
            }
            if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getToLocationId())) {
                doingLocationIds.add(robotTaskDetailDO.getToLocationId());
            }
            if (ObjectUtil.isEmpty(doingLocationIds)) {
                continue;
            }
            //目前仅取一个
            // todo 等将来能控制好多台车机同时往一个点位取，再调整成同时取一个点位的多个库位
            List<WareHouseLocationDO> locationDOS = locationMapper.selectNeedMoveLocation(doingLocationIds);
            if (ObjectUtil.isEmpty(locationDOS)) {
                log.info("暂无需要自动移库的库位 :{}" ,JSON.toJSONString(robotTaskDetailDO));
                continue;
            }
            boolean enableOrLock = locationDOS.stream().anyMatch(v -> LocationEnableEnum.NO.getType().equals(v.getLocationEnable())
                    || LocationLockEnum.NO.getType().equals(v.getLocationLock()));
            if (enableOrLock) {
                log.info("此库位被锁定或者禁用,无法执行移库 :{}",JSON.toJSONString(locationDOS));
                //todo 后期加到告警表里面 方便排查问题
                continue;
            }
            Pair<RobotTaskDetailDO, List<WareHouseLocationDO>> pair = Pair.of(robotTaskDetailDO, locationDOS);
            result.add(pair);
        }
        return result;
    }

    /**
     * 下发任务
     * @param robots
     * @param pairs
     */
    @Transactional(rollbackFor = Exception.class)
    public List<RobotTaskAutoMoveDO> allocationRobot(List<RobotInformationDO> robots, List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> pairs) {
        List<RobotTaskAutoMoveDO> list = new ArrayList<>();
        if (ObjectUtil.isEmpty(robots) || ObjectUtil.isEmpty(pairs)) {
            return list;
        }

        //查找空库位
        // todo 并且 map_item_id没有处理中的任务
        List<WareHouseLocationDO> emptyLocations = locationMapper.selectList(new LambdaQueryWrapperX<WareHouseLocationDO>()
                .isNull(WareHouseLocationDO::getLaneId)
                .eq(WareHouseLocationDO::getLocationUseStatus, LocationUseStatusEnum.NO.getType())
                .eq(WareHouseLocationDO::getLocationEnable, LocationEnableEnum.YES.getType())
                .eq(WareHouseLocationDO::getLocationLock, LocationLockEnum.YES.getType())
                .orderByDesc(WareHouseLocationDO::getLocationNumber));
        if (ObjectUtil.isEmpty(emptyLocations)) {
            log.info("查不到空库位,无法执行移库任务");
            return list;
        }

        for (Pair<RobotTaskDetailDO, List<WareHouseLocationDO>> pair : pairs) {
            RobotTaskDetailDO detailDO = pair.getLeft();
            for (WareHouseLocationDO wareHouseLocationDO : pair.getRight()) {
                if (ObjectUtil.isEmpty(robots) || ObjectUtil.isEmpty(emptyLocations)) {
                    return list;
                }
                for (WareHouseLocationDO emptyLocation : emptyLocations) {
                    // 校验机器人工作区域
                    RobotInformationDO robotInformationDO = robots.stream()
                            .filter(v ->
                                    ObjectUtil.isNotEmpty(v.getMacAddress())
                                            && v.getFloorAreaJson().contains(wareHouseLocationDO.getMapId())
                                            && v.getFloorAreaJson().contains(emptyLocation.getMapId()))
                            .findFirst()
                            .orElse(new RobotInformationDO());
                    if (ObjectUtil.isEmpty(robotInformationDO) || ObjectUtil.isEmpty(robotInformationDO.getRobotNo())) {
                        continue;
                    }

                    RobotTaskAutoMoveDO autoMoveDO = new RobotTaskAutoMoveDO();
                    autoMoveDO.setRobotTaskDetailId(detailDO.getId());
                    autoMoveDO.setRobotTaskId(detailDO.getRobotTaskId());
                    autoMoveDO.setFromLocationId(wareHouseLocationDO.getId());
                    autoMoveDO.setFromLocationNo(wareHouseLocationDO.getLocationNo());
                    autoMoveDO.setToLocationId(emptyLocation.getId());
                    autoMoveDO.setToLocationNo(emptyLocation.getLocationNo());
                    autoMoveDO.setFromLaneId(wareHouseLocationDO.getLaneId());
                    autoMoveDO.setTaskStatus(AutoMoveTaskStatusEnum.DOING.getType());
                    autoMoveDO.setPriority(wareHouseLocationDO.getLocationNumber());
                    autoMoveDO.setRobotNo(robotInformationDO.getRobotNo());
                    autoMoveDO.setTaskType(AutoMoveTaskTypeEnum.AUTO_MOVE.getType());
                    autoMoveDO.setMoveType(AutoMoveTypeEnum.GO.getType());
                    autoMoveDO.setStartTime(LocalDateTime.now());
                    list.add(autoMoveDO);

                    robots.removeIf(v -> v.getRobotNo().equals(autoMoveDO.getRobotNo()));
                    emptyLocations.removeIf(v -> v.getId().equals(autoMoveDO.getToLocationId()));
                }
            }

        }

        return list;
    }

    /**
     * 查询需要移库的库位(线库)
     * @param taskDetailDOS
     * @return
     */
    public List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> getLaneAutoMoveLocation(List<RobotTaskDetailDO> taskDetailDOS) {
        List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> result = new ArrayList<>();
        if (ObjectUtil.isEmpty(taskDetailDOS) || !znConfigConstant.getLaneAutoMove()) {
            log.info("没有线库的任务明细, 不需要执行线库移库");
            return result;
        }
        //此线库不能有移动过来的任务
        List<RobotTaskDetailDO> doingTaskDetail = robotTaskDetailMapper.selectList(new LambdaQueryWrapperX<RobotTaskDetailDO>()
                .eq(RobotTaskDetailDO::getTaskStatus, (RobotTaskStatusEnum.DOING.getType()))
                .in(RobotTaskDetailDO::getTaskType, RobotTaskTypeEnum.TAKE_RELEASE.getType(), RobotTaskTypeEnum.TAKE.getType()));

        Set<Long> doingTaskDetailLaneIds = new HashSet<>();
        if (ObjectUtil.isNotEmpty(doingTaskDetail)) {
            for (RobotTaskDetailDO robotTaskDetailDO : doingTaskDetail) {
                if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLaneId())) {
                    doingTaskDetailLaneIds.add(robotTaskDetailDO.getFromLaneId());
                }
                if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getToLaneId())) {
                    doingTaskDetailLaneIds.add(robotTaskDetailDO.getToLaneId());
                }
            }
        }

        //自动移库的任务
        List<RobotTaskAutoMoveDO> robotTaskAutoMoveDOS = taskAutoMoveMapper.selectList(new LambdaQueryWrapperX<RobotTaskAutoMoveDO>()
                .eq(RobotTaskAutoMoveDO::getTaskStatus, RobotTaskStatusEnum.DOING.getType()));
        Set<Long> doingAutoMoveLaneIds = new HashSet<>();
        Set<Long> doingDetailIds = new HashSet<>();
        if (ObjectUtil.isNotEmpty(robotTaskAutoMoveDOS)) {
            robotTaskAutoMoveDOS.forEach(robotTaskAutoMoveDO -> {
                if (ObjectUtil.isNotEmpty(robotTaskAutoMoveDO.getFromLaneId())) {
                    doingAutoMoveLaneIds.add(robotTaskAutoMoveDO.getFromLaneId());
                }
                if (ObjectUtil.isNotEmpty(robotTaskAutoMoveDO.getRobotTaskDetailId())) {
                    doingDetailIds.add(robotTaskAutoMoveDO.getRobotTaskDetailId());
                }
            });
        }

        //只有库位为线库，且任务类型为取放货和仅取货的需要移库
        List<RobotTaskDetailDO> details = taskDetailDOS.stream().filter(v ->
                        (RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(v.getTaskType())
                                || RobotTaskTypeEnum.TAKE.getType().equals(v.getTaskType()))
                                && ObjectUtil.isNotEmpty(v.getFromLaneId())
                                && (/*ObjectUtil.isNotEmpty(doingAutoMoveLaneIds) &&*/ !doingAutoMoveLaneIds.contains(v.getFromLaneId()))
                                && (/*ObjectUtil.isNotEmpty(doingDetailIds) &&*/ !doingDetailIds.contains(v.getId()))
                                && (/*ObjectUtil.isNotEmpty(doingTaskDetailLaneIds) &&*/ !doingTaskDetailLaneIds.contains(v.getFromLaneId())))
                .collect(Collectors.toList());
        if (ObjectUtil.isEmpty(details)) {
            log.info("没有线库的任务, 不需要执行线库移库");
            return result;
        }

        Map<Long, List<RobotTaskDetailDO>> detailMaps =
                details.stream().collect(Collectors.groupingBy(RobotTaskDetailDO::getFromLaneId));
        //查找每一个线库最小的一个库位
        // todo 等将来能控制好多台车机同时往一个线库取，再调整成同时取一个线库的多个库位
        for (Map.Entry<Long, List<RobotTaskDetailDO>> detaiMap : detailMaps.entrySet()) {
            List<WareHouseLocationDO> locations = new ArrayList<>();
            RobotTaskDetailDO robotTaskDetail = detaiMap.getValue()
                    .stream()
                    .min(Comparator.comparing(RobotTaskDetailDO::getFromLocationNumber)).get();
            if (ObjectUtil.isNotEmpty(robotTaskDetail) && ObjectUtil.isNotEmpty(robotTaskDetail.getFromLocationNumber())) {
                WareHouseLocationDO location = WareHouseLocationDO.builder().locationNumber(robotTaskDetail.getFromLocationNumber())
                        .locationUseStatus(LocationUseStatusEnum.YES.getType())
//                        .locationLock(LocationLockEnum.YES.getType())
                        .laneId(detaiMap.getKey()).build();
                locations.add(location);
            }

            log.info("查询是否需要执行移库任务的线库id和对应的排序位置 :{}", JSON.toJSONString(locations));
            if (ObjectUtil.isEmpty(locations)) {
                continue;
            }
            List<WareHouseLocationDO> wareHouseLocationDOS = locationMapper.selectLocationByList(locations);
            if (ObjectUtil.isEmpty(wareHouseLocationDOS)) {
                continue;
            }
            WareHouseLocationDO wareHouseLocationDO = wareHouseLocationDOS.get(0);
            if (LocationEnableEnum.YES.getType().equals(wareHouseLocationDO.getLocationEnable())
                && LocationLockEnum.YES.getType().equals(wareHouseLocationDO.getLocationLock())) {
                Pair<RobotTaskDetailDO, List<WareHouseLocationDO>> pair = Pair.of(robotTaskDetail, Lists.newArrayList(wareHouseLocationDO));
                result.add(pair);
            }else {
                log.info("线库--此库位被锁定或者禁用,无法执行移库 :{}",JSON.toJSONString(wareHouseLocationDO));
            }
        }

        return result;
    }

























}