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

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.api.robot.websocket.RobotStatusDataPoseDTO;
import cn.iocoder.yudao.module.system.config.poperties.ZnConfigConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotStatusCodeConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.controller.admin.config.vo.CommonConfigVO;
import cn.iocoder.yudao.module.system.dal.dataobject.config.CommonConfigDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
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.config.CommonConfigMapper;
import cn.iocoder.yudao.module.system.dal.mysql.remote.RemoteControllerInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotInformationMapper;
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.config.CommandConfigTypeEnum;
import cn.iocoder.yudao.module.system.enums.robot.*;
import cn.iocoder.yudao.module.system.enums.robot.charge.ChargeModelEnum;
import cn.iocoder.yudao.module.system.util.redis.RedisUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
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 org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DistributeTasksServiceImpl implements DistributeTasksService {

    @Autowired
    private RobotInformationMapper robotInformationMapper;

    @Autowired
    private RobotTaskMapper robotTaskMapper;

    @Autowired
    private RobotTaskDetailMapper robotTaskDetailMapper;

    @Resource
    private CommonConfigMapper configMapper;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private RemoteControllerInformationMapper controllerInformationMapper;

    @Autowired
    private ZnConfigConstant znConfigConstant;


    /**
     * 获取待执行的机器人和任务
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> getRobotAndTaskDetails() {
        TenantContextHolder.setTenantId(1L);
        Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> pair = Pair.of(new ArrayList<>(), new ArrayList<>());

        CommonConfigDO commonConfigDO = configMapper.selectOne(new LambdaQueryWrapper<CommonConfigDO>()
                .eq(CommonConfigDO::getConfigType, CommandConfigTypeEnum.CHARG_CONFIG.getType()));

        /*Boolean needDoCharge = needDoCharge(commonConfigDO);
        if (needDoCharge) {
            log.info("所有机器人都需要去执行充电任务");
            return pair;
        }*/

        List<String> remoteRobotNos = controllerInformationMapper.getRemoteControllerRobotNos();
        log.info("远遥中的车辆 :{}",JSON.toJSONString(remoteRobotNos));

        List<RobotInformationDO> robots = robotInformationMapper.selectList(new LambdaQueryWrapperX<RobotInformationDO>()
                .in(RobotInformationDO::getRobotStatus, RobotStatusEnum.STAND_BY.getType(), RobotStatusEnum.CHARGE.getType())
                .eq(RobotInformationDO::getRobotTaskModel, RobotTaskModelEnum.NORMAL.getType()));

        if (ObjectUtil.isEmpty(robots)) {
            log.info("暂无空闲的机器人");
            return pair;
        }

        //机器人最后做的任务
        /*List<RobotTaskDetailDO> lastTaskDetails = robotTaskDetailMapper.getLastTaskGroupByRobotNo();
        Map<String, RobotTaskDetailDO> lastTaskDetailMap = null;
        if (ObjectUtil.isNotEmpty(lastTaskDetails)) {
            lastTaskDetailMap = lastTaskDetails.stream().collect(Collectors.toMap(v -> v.getRobotNo(), Function.identity()));
        }*/

        CommonConfigVO chargeConfig = JSONUtil.toBean(commonConfigDO.getConfigStr(), CommonConfigVO.class);

        List<RobotInformationDO> taskRobots = new ArrayList<>();
        for (RobotInformationDO robot : robots) {

            if (ObjectUtil.isNotEmpty(remoteRobotNos) && remoteRobotNos.contains(robot.getRobotNo())) {
                continue;
            }

            String taskStatusKey = RobotTaskChcheConstant.ROBOT_TASK_STATUS + robot.getMacAddress();

            Object taskStatus = redisUtil.get(taskStatusKey);
            if (ObjectUtil.isEmpty(taskStatus) || !RobotStatusCodeConstant.CAN_DO_TASK.equals(Boolean.parseBoolean(String.valueOf(taskStatus)))) {
                robot.setRobotStatus(RobotStatusEnum.DOING.getType());
                log.info("车机上报不允许接任务 :{}",robot.getRobotNo());
                continue;
            }


            String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT + robot.getMacAddress();
            Object pose = redisUtil.get(pose2dKey);
            if (ObjectUtil.isEmpty(pose)) {
                log.info("车辆没有点位信息 :{}",robot.getRobotNo());
                continue;
            }

            //取货的，先执行放货再去充电
            /*if (ObjectUtil.isNotEmpty(lastTaskDetailMap) && ObjectUtil.isNotEmpty(lastTaskDetailMap.get(robot.getRobotNo()))) {
                robot.setRobotStatus(RobotStatusEnum.LAST_TASK_IS_TAKE.getType());
                continue;
            }*/

            if (ObjectUtil.isNotEmpty(chargeConfig) && ObjectUtil.isNotEmpty(chargeConfig.getStartAutoCharge())) {
                String socKey = RobotTaskChcheConstant.ROBOT_INFORMATION_SOC +robot.getMacAddress();
                Object socObject = redisUtil.get(socKey);

                if (ObjectUtil.isEmpty(socObject)) {
                    robot.setRobotStatus(RobotStatusEnum.DOING.getType());
                    log.info("车机没有电量信息 :{}",robot.getRobotNo());
                    continue;
                }
                BigDecimal robotRemainingElectricity = new BigDecimal(socObject.toString());
                BigDecimal robotEndElectricity = new BigDecimal(chargeConfig.getStartAutoCharge()+"");
                if (robotRemainingElectricity.compareTo(robotEndElectricity) < 0) {
                    robot.setRobotStatus(RobotStatusEnum.DOING.getType());
                    log.info("机器人的电量少于自动充电电量，不能接任务 :{}",robot.getRobotNo());
                    continue;
                }
            }


            //自动充电的车子，电量到达设定的阀值，也能执行任务
            /*if (ChargeTypeEnum.AUTOMATIC.getType().equals(robot.getChargeType())
                    && RobotStatusEnum.CHARGE.getType().equals(robot.getRobotStatus())) {
                setRobotStatus(commonConfigDO, robot);
            }*/
            Boolean b = remainingElectricityBigger(chargeConfig, robot);
            if (b) {
                robot.setRobotStatus(RobotStatusEnum.STAND_BY.getType());
                taskRobots.add(robot);
            }
        }

        /*robots = robots.stream()
                .filter(v -> (RobotStatusEnum.STAND_BY.getType().equals(v.getRobotStatus())
                        || RobotStatusEnum.LAST_TASK_IS_TAKE.getType().equals(v.getRobotStatus())))
                .collect(Collectors.toList());*/

        if (taskRobots.isEmpty()) {
            log.info("暂无可用的机器人,可能正在充电,可能车机上报不允许接任务");
            return pair;
        }

        log.info("完成查找车子");

        List<RobotTaskDO> taskDOList = robotTaskMapper.selectList(new LambdaQueryWrapper<RobotTaskDO>()
                .in(RobotTaskDO::getTaskStatus, RobotTaskStatusEnum.DOING.getType(), RobotTaskStatusEnum.NEW.getType()));

        //拼接
        List<Long> montageTaskIds =taskDOList.stream()
                .filter(v -> MontageTaskEnum.YES.getType().equals(v.getMontageTask()))
                .map(RobotTaskDO::getId)
                .collect(Collectors.toList());

        //不拼接
        List<Long> singleTaskIds =taskDOList.stream()
                .filter(v -> MontageTaskEnum.NO.getType().equals(v.getMontageTask()))
                .map(RobotTaskDO::getId)
                .collect(Collectors.toList());

        log.info("完成查找主任务");

        if (ObjectUtil.isEmpty(montageTaskIds) && ObjectUtil.isEmpty(singleTaskIds)) {
            log.info("暂无需要处理的主任务");
            return ImmutablePair.of(taskRobots, null);
        }

        List<RobotTaskDetailDO> taskDetails = getTaskDetail(montageTaskIds, singleTaskIds);

        if (taskDetails.isEmpty()) {
            log.info("暂无需要处理的明细任务");
            return ImmutablePair.of(taskRobots, null);
        }
        return ImmutablePair.of(taskRobots, taskDetails);
    }

    /**
     * true 需要去充电
     *
     * @param commonConfigDO
     * @return
     */
    public Boolean needDoCharge(CommonConfigDO commonConfigDO) {
        if (ObjectUtil.isEmpty(commonConfigDO) || ObjectUtil.isEmpty(commonConfigDO.getConfigStr())) {
            return false;
        }

        CommonConfigVO chargeConfig = JSONUtil.toBean(commonConfigDO.getConfigStr(), CommonConfigVO.class);
        LocalDateTime now = LocalDateTime.now();

        if (ZeroOneEnum.ZERO.getType().equals(chargeConfig.getEveryDay())
                && now.isAfter(chargeConfig.getOnceStartTime()) && now.isBefore(chargeConfig.getOnceEndTime())) {
            return true;
        } else if (ZeroOneEnum.ONE.getType().equals(chargeConfig.getEveryDay())
                && now.isBefore(chargeConfig.getScheduleChargeEndTime())
                && now.isAfter(chargeConfig.getScheduleChargeStartTime())) {
            return true;
        }

        return false;
    }

    /**
     * 剩余电量是否大于设置的充电阀值
     *
     * @param chargeConfig
     * @param robot
     */
    private Boolean remainingElectricityBigger(CommonConfigVO chargeConfig, RobotInformationDO robot) {
        String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT + robot.getMacAddress();
        String chargeModelKey = RobotTaskChcheConstant.ROBOT_CHARGE_MODEL + robot.getRobotNo();
        Object chargeModelCache = redisUtil.get(chargeModelKey);
        Object poseCache = redisUtil.get(pose2dKey);
        RobotStatusDataPoseDTO dataPoseDTO = JSON.parseObject((String) poseCache, RobotStatusDataPoseDTO.class);
        log.info("充电机器人编号:{} ,信息: {}", robot.getRobotNo(), JSON.toJSONString(dataPoseDTO));

        String socKey = RobotTaskChcheConstant.ROBOT_INFORMATION_SOC +robot.getMacAddress();
        Object o = redisUtil.get(socKey);
        String soc = ObjectUtil.isEmpty(o) ? "0": o.toString();

        if ( ObjectUtil.isEmpty(poseCache) || ObjectUtil.isEmpty(o)) {
            return false;
        }

        //车子剩余电量
        BigDecimal robotRemainingElectricity = new BigDecimal(soc);
        //设置离开的电量
        BigDecimal robotEndElectricity = new BigDecimal("30");

        if (ObjectUtil.isNotEmpty(robot.getAutoCharge()) && ObjectUtil.isNotEmpty(chargeModelCache)
                && !ChargeModelEnum.FULL.getType().equals((Integer) chargeModelCache)) {
            robotEndElectricity = new BigDecimal(String.valueOf(robot.getAutoCharge()));
        } else if (ObjectUtil.isNotEmpty(chargeModelCache) && ChargeModelEnum.FULL.getType().equals((Integer) chargeModelCache)) {
            robotEndElectricity = new BigDecimal(znConfigConstant.getFullElectricity());
        } else if (ObjectUtil.isNotEmpty(chargeModelCache) && ChargeModelEnum.CHANCE.getType().equals((Integer) chargeModelCache)
            && ObjectUtil.isNotEmpty(chargeConfig.getEndAutoCharge())) {
            robotEndElectricity = new BigDecimal(String.valueOf(chargeConfig.getChanceChargeEnd()));
        } else if (ObjectUtil.isNotEmpty(chargeConfig.getEndAutoCharge())){
            robotEndElectricity = new BigDecimal(String.valueOf(chargeConfig.getEndAutoCharge()));
        }

        if (robotRemainingElectricity.compareTo(robotEndElectricity) >= 0) {
            return true;
        }else {
            log.info("机器人正在充电,还没达到充电设置的电量,暂不能接任务:{} ", robot.getRobotNo());
        }
        return false;
    }

    /**
     * 获取明细
     *
     * @param montageTaskIds 拼接
     * @param singleTaskIds 不拼接
     * @return
     */
    private List<RobotTaskDetailDO> getTaskDetail(List<Long> montageTaskIds, List<Long> singleTaskIds) {
        if (ObjectUtil.isNotEmpty(montageTaskIds) && ObjectUtil.isEmpty(singleTaskIds)) {
            return robotTaskDetailMapper.getMontageUnDoTask(montageTaskIds);
        } else if (ObjectUtil.isEmpty(montageTaskIds) && ObjectUtil.isNotEmpty(singleTaskIds)) {
            return robotTaskDetailMapper.getSingleUnDoTask(singleTaskIds);
        }
        return robotTaskDetailMapper.getUnDoTaskDetail(montageTaskIds, singleTaskIds);
    }






}
