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.common.exception.ServiceException;
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.dto.RobotStatusDataPoseDTO;
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.information.DeviceInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotChargeLogDO;
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.information.DeviceInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotChargeLogMapper;
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.device.DeviceTypeEnum;
import cn.iocoder.yudao.module.system.enums.device.DeviceUseStatusEnum;
import cn.iocoder.yudao.module.system.enums.redis.RobotCacheLockEnum;
import cn.iocoder.yudao.module.system.enums.robot.*;
import cn.iocoder.yudao.module.system.enums.robot.charge.ChargeModelEnum;
import cn.iocoder.yudao.module.system.service.robot.pathplanning.RobotPathPlanningService;
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.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.*;
import java.util.stream.Collectors;

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

@Service
@Slf4j
public class AutoChargeServiceImpl implements AutoChargeService {

    @Resource
    private RobotInformationMapper robotInformationMapper;

    @Resource
    private CommonConfigMapper configMapper;

    @Resource
    private RobotChargeLogMapper chargeLogMapper;

    @Resource
    private DeviceInformationMapper deviceInformationMapper;

    @Autowired
    private RobotTaskDetailMapper robotTaskDetailMapper;

    @Autowired
    private RobotTaskMapper robotTaskMapper;

    @Resource
    private RedisUtil redisUtil;

    @Value("${zn.full_electricity:100}")
    private String fullElectricity;

    @Autowired
    private RobotPathPlanningService robotPathPlanningService;

    /**
     * 自动充电
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoChargeJob() {
        TenantContextHolder.setTenantId(1L);

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

        if (ObjectUtil.isEmpty(commonConfigDO)) {
            log.info("暂未配置充电信息");
            return;
        }

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

        List<RobotInformationDO> robots = robotInformationMapper.selectList(new LambdaQueryWrapperX<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotStatus, RobotStatusEnum.STAND_BY.getType())
                .eq(RobotInformationDO::getRobotTaskModel, RobotTaskModelEnum.NORMAL.getType()));
        if (ObjectUtil.isEmpty(robots)) {
            log.info("充电任务,没有空闲机器人");
            return;
        }

        List<DeviceInformationDO> deviceInformationDOS = deviceInformationMapper.selectList(new LambdaQueryWrapperX<DeviceInformationDO>()
                .eq(DeviceInformationDO::getDeviceEnable, ZeroOneEnum.ONE.getType())
                .eq(DeviceInformationDO::getDeviceUseStatus, DeviceUseStatusEnum.IDLE.getType())
                .eq(DeviceInformationDO::getDeviceType, DeviceTypeEnum.CHARGING_STATION.getType()));

        if (ObjectUtil.isEmpty(deviceInformationDOS)) {
            log.info("充电任务,没有空闲的充电桩");
            return;
        }

        List<String> robotNos = robots.stream()
                .map(RobotInformationDO::getRobotNo)
                .collect(Collectors.toList());
        //近期有充满电的机器人编号
        List<String> chargeFullNos = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(chargeConfig.getChanceCycle())) {
            //查询充满电
            chargeFullNos = chargeLogMapper.getChargeFullRobotNos(chargeConfig.getChanceCycle(),robotNos);
        }

        List<RobotTaskDetailDO> taskDetailDOS = robotTaskDetailMapper.getChargeTaskDetail();
        Map<String, List<RobotTaskDetailDO>> detailMap = null;
        if (ObjectUtil.isNotEmpty(taskDetailDOS)) {
            detailMap = taskDetailDOS.stream().collect(Collectors.groupingBy(RobotTaskDetailDO::getRobotNo));
        }

        List<RobotChargeLogDO> logs = new ArrayList<>();
        //组装充电任务
        assembleChargeTask(robots,deviceInformationDOS,chargeConfig,chargeFullNos,logs,detailMap);

        if (ObjectUtil.isEmpty(logs)) {
            log.info("没有需要执行的充电任务");
            return;
        }
        
        chargeLogMapper.insertBatch(logs);

        robotPathPlanningService.sendChargeTaskToPP(logs,taskDetailDOS,robots);

        //改成异步
        logs.stream().forEach(v -> {
            String chargeModelKey = RobotTaskChcheConstant.ROBOT_CHARGE_MODEL +v.getRobotNo();
            redisUtil.set(chargeModelKey,v.getChargeModel());
        });

    }

    /**
     * 组装充电任务
     * @param robots
     * @param deviceInformationDOS
     * @param chargeConfig
     * @param chargeFullNos
     * @param logs
     * @param detailMap
     */
    public void assembleChargeTask(List<RobotInformationDO> robots, List<DeviceInformationDO> deviceInformationDOS,
                                   CommonConfigVO chargeConfig, List<String> chargeFullNos,
                                   List<RobotChargeLogDO> logs, Map<String, List<RobotTaskDetailDO>> detailMap) {
        //判断机器人身上的电量少于设定的电量
        for (RobotInformationDO robot : robots) {
            String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT_SOC +robot.getMacAddress();
            Object poseCache = redisUtil.get(pose2dKey);
            RobotStatusDataPoseDTO dataPoseDTO= JSON.parseObject((String)poseCache, RobotStatusDataPoseDTO.class);
            if (ObjectUtil.isEmpty(dataPoseDTO) || ObjectUtil.isEmpty(dataPoseDTO.getBat_soc())) {
                log.info("当前机器人查不到电量信息,所以不执行充电 :{}",robot.getRobotNo());
                continue;
            }

            if (ObjectUtil.isEmpty(deviceInformationDOS)) {
                log.info("充电桩分配完成 :{}",robot.getRobotNo());
                break;
            }

            DeviceInformationDO deviceInformationDO = deviceInformationDOS.stream()
                    .filter(v -> v.getDeviceAttribute().equals(robot.getChargeType())
                        && robot.getFloorAreaJson().contains(v.getPositionMapId()))
                    .findFirst()
                    .orElse(new DeviceInformationDO());
            if (ObjectUtil.isEmpty(deviceInformationDO.getDeviceNo())) {
                log.info("当前机器人查不到对应的充电桩类型、或者机器人不能在此区域充电 :{}",robot.getRobotNo());
                continue;
            }

            RobotChargeLogDO logDo = new RobotChargeLogDO();
            logDo.setRobotNo(robot.getRobotNo());
            logDo.setDeviceNo(deviceInformationDO.getDeviceNo());
            logDo.setPositionMapItemId(deviceInformationDO.getPositionMapItemId());
            String[] split = dataPoseDTO.getBat_soc().split("\\.");
            logDo.setStartElectricity(Integer.valueOf(split[0]));
            //配置的充满电电量
            BigDecimal robotFullElectricity = new BigDecimal(fullElectricity);
            //车子剩余电量
            BigDecimal robotRemainingElectricity = new BigDecimal(split[0]);
            if (ObjectUtil.isNotEmpty(chargeConfig.getChanceCycle()) && !chargeFullNos.contains(robot.getRobotNo())
                    && robotRemainingElectricity.compareTo(robotFullElectricity) < 0) {
                //充满电
                logDo.setChargeModel(ChargeModelEnum.FULL.getType());
                logs.add(logDo);
                deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                log.info("分配到充满电充电任务 :{} ,充电设备编号是 :{}, 任务id :{}",logDo.getRobotNo(),logDo.getDeviceNo(),logDo.getTaskDetailId());
                continue;
            }

            if (ObjectUtil.isNotEmpty(detailMap) && detailMap.containsKey(robot.getRobotNo())
                    && ObjectUtil.isNotEmpty(robot.getAutoCharge())) {
                //任务充电
                BigDecimal robotConfigElectricity = new BigDecimal(String.valueOf(robot.getAutoCharge()));
                if (robotRemainingElectricity.compareTo(robotConfigElectricity) < 0) {
                    RobotTaskDetailDO robotTaskDetailDO = detailMap.get(robot.getRobotNo()).get(0);
                    deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                    logDo.setChargeModel(ChargeModelEnum.TASK.getType());
                    logDo.setTaskDetailId(robotTaskDetailDO.getId());
                    logs.add(logDo);
                    log.info("分配到任务充电任务 :{} ,充电设备编号是 :{}, 任务id :{}",logDo.getRobotNo(),logDo.getDeviceNo(),logDo.getTaskDetailId());
                    continue;
                }
            }

            if (ObjectUtil.isNotEmpty(detailMap) && detailMap.containsKey(robot.getRobotNo())
                    && ObjectUtil.isEmpty(robot.getAutoCharge()) && ObjectUtil.isNotEmpty(chargeConfig.getStartAutoCharge())) {
                //任务充电
                BigDecimal robotConfigElectricity = new BigDecimal(String.valueOf(chargeConfig.getStartAutoCharge()));
                if (robotRemainingElectricity.compareTo(robotConfigElectricity) < 0) {
                    RobotTaskDetailDO robotTaskDetailDO = detailMap.get(robot.getRobotNo()).get(0);
                    deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                    logDo.setChargeModel(ChargeModelEnum.TASK.getType());
                    logDo.setTaskDetailId(robotTaskDetailDO.getId());
                    logs.add(logDo);
                    log.info("分配到任务充电任务 :{} ,充电设备编号是 :{}, 任务id :{}",logDo.getRobotNo(),logDo.getDeviceNo(),logDo.getTaskDetailId());
                    continue;
                }
            }

            if (ObjectUtil.isNotEmpty(detailMap) && detailMap.containsKey(robot.getRobotNo())
                    && ObjectUtil.isEmpty(robot.getAutoCharge()) && ObjectUtil.isNotEmpty(chargeConfig.getChanceChargeStart())) {
                //任务充电
                BigDecimal robotConfigElectricity = new BigDecimal(String.valueOf(chargeConfig.getChanceChargeStart()));
                if (robotRemainingElectricity.compareTo(robotConfigElectricity) < 0) {
                    RobotTaskDetailDO robotTaskDetailDO = detailMap.get(robot.getRobotNo()).get(0);
                    deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                    logDo.setChargeModel(ChargeModelEnum.TASK.getType());
                    logDo.setTaskDetailId(robotTaskDetailDO.getId());
                    logs.add(logDo);
                    log.info("分配到任务充电任务 :{} ,充电设备编号是 :{}, 任务id :{}",logDo.getRobotNo(),logDo.getDeviceNo(),logDo.getTaskDetailId());
                    continue;
                }
            }

            if (ObjectUtil.isNotEmpty(robot.getAutoCharge())) {
                //自动充电
                BigDecimal robotConfigElectricity = new BigDecimal(String.valueOf(robot.getAutoCharge()));
                if (robotRemainingElectricity.compareTo(robotConfigElectricity) < 0) {
                    logDo.setChargeModel(ChargeModelEnum.AUTO.getType());
                    logs.add(logDo);
                    deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                    log.info("分配到自动充电任务 :{} ,充电设备编号是 :{}",logDo.getRobotNo(),logDo.getDeviceNo());
                    continue;
                }
            }

            if (ObjectUtil.isNotEmpty(chargeConfig.getStartAutoCharge())) {
                //自动充电
                BigDecimal robotConfigElectricity = new BigDecimal(String.valueOf(chargeConfig.getStartAutoCharge()));
                if (robotRemainingElectricity.compareTo(robotConfigElectricity) < 0) {
                    logDo.setChargeModel(ChargeModelEnum.AUTO.getType());
                    logs.add(logDo);
                    deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                    log.info("分配到自动充电任务 :{} ,充电设备编号是 :{}",logDo.getRobotNo(),logDo.getDeviceNo());
                    continue;
                }
            }

            if (ObjectUtil.isNotEmpty(chargeConfig.getChanceChargeStart())) {
                //机会充电
                BigDecimal robotConfigElectricity = new BigDecimal(String.valueOf(chargeConfig.getChanceChargeStart()));
                if (robotRemainingElectricity.compareTo(robotConfigElectricity) < 0) {
                    logDo.setChargeModel(ChargeModelEnum.CHANCE.getType());
                    logs.add(logDo);
                    deviceInformationDOS.removeIf(v -> logDo.getDeviceNo().equals(v.getDeviceNo()));
                    log.info("分配到机会充电任务 :{} ,充电设备编号是 :{}",logDo.getRobotNo(),logDo.getDeviceNo());
                }
            }
        }
    }

}
