package cn.iocoder.yudao.module.system.api.robot;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import cn.iocoder.yudao.module.mqtt.api.path.PathPlanningApi;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataErrorDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataPoseDTO;
import cn.iocoder.yudao.module.system.api.robot.vo.RobotInformationVO;
import cn.iocoder.yudao.module.system.constant.path.PathPlanningTopicConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.constant.webSocket.WebSocketConstant;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotWarnCodeMappingDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotWarnMsgDO;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotWarnCodeMappingMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotWarnMsgMapper;
import cn.iocoder.yudao.module.system.enums.robot.RobotWarnType;
import cn.iocoder.yudao.module.system.service.robot.RobotInformationService;
import cn.iocoder.yudao.module.system.service.robot.RobotWarnMsgService;
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.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@RestController // 提供 RESTful API 接口，给 Feign 调用
@Validated
public class RobotStatusApiImpl implements RobotStatusApi {

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private RobotInformationService robotInformationService;

    @Resource
    private RobotWarnCodeMappingMapper warnCodeMappingMapper;

    @Resource
    private RobotWarnMsgMapper warnMsgMapper;

    @Value("${zn.robot_position_cache_time:600}")
    private Long robotPositionCacheTime;

    @Value("${zn.robot_error_level_time:30}")
    private Long robotErrorLevelTime;

    @Resource
    private RequestProcessor processor;

    @Resource
    private PathPlanningApi pathPlanningApi;

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private RobotWarnMsgService warnMsgService;


    public void doRobotStatusUpdate(RobotStatusDTO robotStatusDataDTO) {
        TenantContextHolder.setTenantId(1L);
        if (ObjectUtil.isEmpty(robotStatusDataDTO) || ObjectUtil.isEmpty(robotStatusDataDTO.getMac())) {
            log.info("机器人上报的信息不全 :{}", JSON.toJSONString(robotStatusDataDTO));
            return;
        }

        String robotNo = robotInformationService.getRobotNoByMac(robotStatusDataDTO.getMac());
        String taskStatusKey = RobotTaskChcheConstant.ROBOT_TASK_STATUS + robotStatusDataDTO.getMac();
        String cargoDetectedKey = RobotTaskChcheConstant.ROBOT_CARGO_DETECTED + robotStatusDataDTO.getMac();
        String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT_SOC + robotStatusDataDTO.getMac();
        // todo 后续需要改为从车机上报

        /*redisUtil.set(taskStatusKey, robotStatusDataDTO.getData().getTask_status(), robotPositionCacheTime);
        redisUtil.set(cargoDetectedKey, robotStatusDataDTO.getData().getCargo_detected(), robotPositionCacheTime);*/
        redisUtil.set(taskStatusKey, "IDLE", robotPositionCacheTime);
        redisUtil.set(cargoDetectedKey, false, robotPositionCacheTime);

        Object object = redisUtil.get(pose2dKey);
        RobotStatusDataPoseDTO robotStatusDataPoseDTO = JSONUtil.toBean((String) object, RobotStatusDataPoseDTO.class);

        if (ObjectUtil.isNotEmpty(robotStatusDataDTO.getData())
                && ObjectUtil.isNotEmpty(robotStatusDataDTO.getData().getPose2d())) {
            robotStatusDataPoseDTO.setX(robotStatusDataDTO.getData().getPose2d().getX());
            robotStatusDataPoseDTO.setY(robotStatusDataDTO.getData().getPose2d().getY());
            robotStatusDataPoseDTO.setYaw(robotStatusDataDTO.getData().getPose2d().getYaw());
        }

        if (ObjectUtil.isNotEmpty(robotStatusDataDTO.getData())
                && ObjectUtil.isNotEmpty(robotStatusDataDTO.getData().getFloor_zone())) {
            robotStatusDataPoseDTO.setFloor(robotStatusDataDTO.getData().getFloor_zone().getFloor());
            robotStatusDataPoseDTO.setArea(robotStatusDataDTO.getData().getFloor_zone().getArea());
        }

        robotStatusDataPoseDTO.setFloor("1");
        robotStatusDataPoseDTO.setArea("D区");

        robotStatusDataPoseDTO.setRobotNo(robotNo);
        redisUtil.set(pose2dKey, JSON.toJSONString(robotStatusDataPoseDTO), robotPositionCacheTime);
        pathPlanningApi.synchronousLineObject(robotStatusDataPoseDTO, PathPlanningTopicConstant.AGV_POSE);
        // -- 通过mac 地址获取车辆信息 - (并且加入到缓存中)
        Map<String, RobotInformationVO> robotInformationVOS = robotInformationService.getAllRobotByRedis();
        RobotInformationVO robotInformationVO = robotInformationVOS.get(robotStatusDataDTO.getMac());
        if (robotInformationVO == null) {
            robotInformationVO = robotInformationService.getRobotByRedis(robotStatusDataDTO.getMac());
        }
        robotInformationVO.setPose2d(robotStatusDataDTO.getData().getPose2d());
        // 合并请求 - 这里接受到的数据都丢给 RequestProcessor - 再整合数据通过WebSocket丢给前端
        processor.handleRequest(robotStatusDataPoseDTO.getFloor() + "_" + robotStatusDataPoseDTO.getArea(),
                robotStatusDataDTO.getMac(), JSONUtil.toJsonStr(robotInformationVO));


        if (ObjectUtil.isNotNull(robotStatusDataDTO.getData().getErr_code())) {
            List<RobotStatusDataErrorDTO> errCode = robotStatusDataDTO.getData().getErr_code();

            if (ObjectUtil.isNull(robotNo)) {
                log.info("查不到机器人编号 :{}", robotStatusDataDTO.getMac());
                return;
            }

            List<String> warnCodes =
                    errCode.stream().map(RobotStatusDataErrorDTO::getError_code).collect(Collectors.toList());
            List<RobotWarnCodeMappingDO> robotWarnCodeMappingDOS =
                    warnCodeMappingMapper.selectList(new LambdaQueryWrapper<RobotWarnCodeMappingDO>()
                            .in(RobotWarnCodeMappingDO::getWarnCode, warnCodes));
            if (ObjectUtil.isEmpty(robotWarnCodeMappingDOS)) {
                log.info("查不对应编号的告警信息 :{}", JSON.toJSONString(warnCodes));
                return;
            }

            Map<String, List<RobotWarnCodeMappingDO>> warnCodeMapping =
                    robotWarnCodeMappingDOS.stream().collect(Collectors.groupingBy(RobotWarnCodeMappingDO::getWarnCode));

            List<RobotWarnMsgDO> warnMsgDOS = new ArrayList<>();

            //机器人异常等级
            String errorLevelKey = RobotTaskChcheConstant.ROBOT_ERROR_LEVEL + robotStatusDataDTO.getMac();
            Object errorLevel = redisUtil.get(errorLevelKey);

            String errorMsgKey = RobotTaskChcheConstant.ROBOT_ERROR_MSG + robotStatusDataDTO.getMac();
            Object errorMsg = redisUtil.get(errorMsgKey);

            Integer level = ObjectUtil.isEmpty(errorLevel) ? 0 : Integer.valueOf(errorLevel.toString());

            for (RobotStatusDataErrorDTO robotStatusData : errCode) {
                List<RobotWarnCodeMappingDO> mappingDOS = warnCodeMapping.get(robotStatusData.getError_code());
                if (ObjectUtil.isEmpty(mappingDOS)) {
                    log.info("当前告警类型查不到对应的告警信息 :{}", robotStatusData.getError_code());
                    continue;
                }
                RobotWarnMsgDO warnMsg = RobotWarnMsgDO.builder().warnLevel(Integer.valueOf(robotStatusData.getCode_level()))
                        .warnCode(robotStatusData.getError_code())
                        .robotNo(robotNo)
                        .warnType(RobotWarnType.ROBOT_WARN.getType())
                        .warnMsg(robotNo+"_"+mappingDOS.get(0).getWarnMsg())
                        .warnSolve(mappingDOS.get(0).getWarnSolve())
                        .build();
                warnMsgDOS.add(warnMsg);

                if (level.intValue() < Integer.valueOf(robotStatusData.getCode_level()).intValue()) {
                    level = Integer.valueOf(robotStatusData.getCode_level());
                    errorMsg = warnMsg.getWarnMsg();
                }
            }
            redisUtil.set(errorLevelKey, level, robotErrorLevelTime);
            redisUtil.set(errorMsgKey, errorMsg, robotErrorLevelTime);

            warnMsgService.sendWarnMsgToWebsocket(errorMsg);

            warnMsgMapper.insertBatch(warnMsgDOS);
        }
    }

    /**
     * 更新机器人点位/异常/能否做任务
     *
     * @param robotStatusDataDTO
     * @return
     */
    @Override
    public void robotStatusUpdate(RobotStatusDTO robotStatusDataDTO) {
        taskExecutor.execute(()->{
            doRobotStatusUpdate(robotStatusDataDTO);
        });
    }


}
