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

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
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.common.CommonApi;
import cn.iocoder.yudao.module.mqtt.api.task.dto.RobotAcceptTaskDTO;
import cn.iocoder.yudao.module.mqtt.enums.task.ExecutionTypeEnum;
import cn.iocoder.yudao.module.system.api.path.vo.RobotClosePathPlantingDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotCommandStateDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotCompleteTaskDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataPoseDTO;
import cn.iocoder.yudao.module.system.constant.path.PathPlanningTopicConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotExecutionStateConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTopicConstant;
import cn.iocoder.yudao.module.system.constant.webSocket.WebSocketConstant;
import cn.iocoder.yudao.module.system.dal.dataobject.actionlog.RobotTaskDetailActionLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.config.CommonConfigDO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseLocationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotChargeLogDO;
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.dataobject.robot.RobotWarnMsgDO;
import cn.iocoder.yudao.module.system.dal.mysql.actionlog.RobotTaskDetailActionLogMapper;
import cn.iocoder.yudao.module.system.dal.mysql.cycle.TaskCycleMapper;
import cn.iocoder.yudao.module.system.dal.mysql.houselocation.WareHouseLocationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.information.DeviceInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.*;
import cn.iocoder.yudao.module.system.enums.common.ZeroOneEnum;
import cn.iocoder.yudao.module.system.enums.device.DeviceUseStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.*;
import cn.iocoder.yudao.module.system.enums.robot.charge.ChargeTaskStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotStatusCodeEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotTaksOrderTypeEnum;
import cn.iocoder.yudao.module.system.service.robot.RobotInformationService;
import cn.iocoder.yudao.module.system.service.robot.RobotTaskDetailService;
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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.baomidou.mybatisplus.core.toolkit.IdWorker.getId;

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

    @Autowired
    private RobotTaskDetailMapper robotTaskDetailMapper;

    @Autowired
    private RobotTaskMapper robotTaskMapper;

    @Autowired
    @Resource
    private RobotInformationMapper robotInformationMapper;

    @Resource
    private RobotTaskDetailActionLogMapper taskDetailActionLogMapper;

    @Autowired
    private RobotInformationService robotInformationService;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Resource
    private TaskCycleMapper taskCycleMapper;

    @Resource
    private WareHouseLocationMapper locationMapper;

    @Resource
    private RobotChargeLogMapper chargeLogMapper;

    @Resource
    private CommonApi commonApi;

    @Resource
    public WebSocketSenderApi webSocketSenderApi;

    @Resource
    private RobotTaskDetailService taskDetailService;

    @Resource
    private RobotWarnMsgMapper warnMsgMapper;

    @Resource
    private RobotWarnMsgService warnMsgService;

    @Resource
    private DeviceInformationMapper deviceInformationMapper;

    @Value("${zn.robot_doing_action.doing_action_cache_time:3*60*60}")
    private Long doingActionCacheTime;

    @Transactional(rollbackFor = Exception.class)
    public void doRobotDoneTask(RobotCompleteTaskDTO robotCompleteTaskDTO) {
        log.info("机器人完成任务上报 :{}", JSON.toJSONString(robotCompleteTaskDTO));
        TenantContextHolder.setTenantId(1L);
        String robotDoingActionKey = RobotTaskChcheConstant.ROBOT_QUERY_DOING_ACTION + robotCompleteTaskDTO.getMac();

        if (!RobotStatusCodeEnum.SUCCESS.getType().equals(robotCompleteTaskDTO.getStatus_code())) {
            log.info("车机上报异常 :{}", JSON.toJSONString(robotCompleteTaskDTO));
            closeTask(robotCompleteTaskDTO);
            redisUtil.del(robotDoingActionKey);
            return;
        }

        if (RobotExecutionStateConstant.UN_DO.equals(robotCompleteTaskDTO.getExecution_state())) {
            return;
        }

        if (RobotExecutionStateConstant.DOING.equals(robotCompleteTaskDTO.getExecution_state())
                && RobotTaksOrderTypeEnum.TASK.getType().equals(robotCompleteTaskDTO.getOrder_type())) {
            taskDoing(robotCompleteTaskDTO, robotDoingActionKey);

        } else if (RobotExecutionStateConstant.DONE.equals(robotCompleteTaskDTO.getExecution_state())
                && RobotTaksOrderTypeEnum.TASK.getType().equals(robotCompleteTaskDTO.getOrder_type())) {
            taskDone(robotCompleteTaskDTO);
            redisUtil.del(robotDoingActionKey);
        } else if (RobotExecutionStateConstant.DOING.equals(robotCompleteTaskDTO.getExecution_state())
                && RobotTaksOrderTypeEnum.AUTO_CHARGE.getType().equals(robotCompleteTaskDTO.getOrder_type())) {
            chargeDoing(robotCompleteTaskDTO, robotDoingActionKey);

        } else if (RobotExecutionStateConstant.DONE.equals(robotCompleteTaskDTO.getExecution_state())
                && RobotTaksOrderTypeEnum.AUTO_CHARGE.getType().equals(robotCompleteTaskDTO.getOrder_type())) {
            chargeDone(robotCompleteTaskDTO, robotDoingActionKey);
        }
    }

    /**
     * 处理机器人上报的异常
     *
     * @param robotCompleteTaskDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void closeTask(RobotCompleteTaskDTO robotCompleteTaskDTO) {
        String robotNo = robotInformationService.getRobotNoByMac(robotCompleteTaskDTO.getMac());
        RobotClosePathPlantingDTO closePathPlanting = RobotClosePathPlantingDTO.builder()
                .robotNo(robotNo)
                .id(robotCompleteTaskDTO.getOrder_id().toString())
                .build();
        commonApi.commonMethod(closePathPlanting, PathPlanningTopicConstant.KILL_TASK);
        log.info("车机上报异常， 通知PP把任务取消 :{}", robotCompleteTaskDTO.getOrder_id());

        if (RobotTaksOrderTypeEnum.TASK.getType().equals(robotCompleteTaskDTO.getOrder_type())) {
            RobotTaskDetailDO robotTaskDetailDO = closeTaskDetail(robotCompleteTaskDTO.getOrder_id());
            List<Long> locationIds = new ArrayList<>();
            if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLocationId())) {
                locationIds.add(robotTaskDetailDO.getFromLocationId());
            }
            if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getToLocationId())) {
                locationIds.add(robotTaskDetailDO.getToLocationId());
            }
            if (ObjectUtil.isNotEmpty(locationIds)) {
                locationMapper.releaseLocationLockList(locationIds, robotTaskDetailDO.getRobotTaskId()
                        , LocationLockEnum.YES.getType());
            }

        } else if (RobotTaksOrderTypeEnum.AUTO_CHARGE.getType().equals(robotCompleteTaskDTO.getOrder_type())) {

            RobotChargeLogDO robotChargeLogDO = chargeLogMapper.selectOne(new LambdaQueryWrapper<RobotChargeLogDO>()
                    .eq(RobotChargeLogDO::getId, robotCompleteTaskDTO.getOrder_id()));
            robotChargeLogDO.setTaskStatus(ChargeTaskStatusEnum.ABNORMAL.getType());
            chargeLogMapper.updateById(robotChargeLogDO);

            if (ObjectUtil.isNotEmpty(robotChargeLogDO.getTaskDetailId())) {
                closeTaskDetail(robotCompleteTaskDTO.getOrder_id());
            }
            deviceInformationMapper.updateDeviceUseStatus(robotChargeLogDO.getDeviceNo(),
                    DeviceUseStatusEnum.IDLE.getType(), ZeroOneEnum.ZERO.getType());
        }

        RobotWarnMsgDO warnMsg = RobotWarnMsgDO.builder().warnLevel(4)
                .warnCode(robotCompleteTaskDTO.getStatus_code())
                .robotNo(robotNo)
                .warnType(RobotWarnType.ROBOT_WARN.getType())
                .warnMsg(robotNo +"_"+robotCompleteTaskDTO.getMessage())
                .warnSolve(robotCompleteTaskDTO.getSolution())
                .build();
        warnMsgMapper.insert(warnMsg);

        warnMsgService.sendWarnMsgToWebsocket(warnMsg.getWarnMsg());
    }

    /**
     * 关闭订单
     * @param taskDetailId
     */
    public RobotTaskDetailDO closeTaskDetail(Long taskDetailId) {
        return taskDetailService.closeTaskDetail(taskDetailId);
    }

    /**
     * 车辆正在充电
     *
     * @param robotCompleteTaskDTO
     */
    private void chargeDone(RobotCompleteTaskDTO robotCompleteTaskDTO, String robotDoingActionKey) {
        RobotTaskDetailActionLogDO logOne = new RobotTaskDetailActionLogDO();
        logOne.setCommandId(getId());
        logOne.setActionMsg("车辆正在充电");
        String robotNo = robotInformationService.getRobotNoByMac(robotCompleteTaskDTO.getMac());
        logOne.setRobotNo(robotNo);
        logOne.setTaskDetailId(robotCompleteTaskDTO.getOrder_id());
        taskDetailActionLogMapper.insert(logOne);
        redisUtil.set(robotDoingActionKey, logOne.getActionMsg(), doingActionCacheTime);
    }


    /**
     * 处理充电中
     *
     * @param robotCompleteTaskDTO
     */
    private void chargeDoing(RobotCompleteTaskDTO robotCompleteTaskDTO, String robotDoingActionKey) {
        List<RobotCommandStateDTO> commandState = robotCompleteTaskDTO.getCommand_state();
        if (ObjectUtil.isEmpty(commandState)) {
            return;
        }
        addActionLog(commandState, robotDoingActionKey);
    }

    /**
     * 任务完成
     *
     * @param robotCompleteTaskDTO
     */
    private void taskDone(RobotCompleteTaskDTO robotCompleteTaskDTO) {
        //更新任务状态
        RobotTaskDetailDO detailDO = new RobotTaskDetailDO();
        detailDO.setId(robotCompleteTaskDTO.getOrder_id());
        detailDO.setEndTime(LocalDateTime.now());
        detailDO.setTaskStatus(robotCompleteTaskDTO.getExecution_state());
        robotTaskDetailMapper.updateRobotDetailById(detailDO);
        //更新任务状态
        RobotTaskDetailDO robotTaskDetailDO = robotTaskDetailMapper.selectById(robotCompleteTaskDTO.getOrder_id());
        List<RobotTaskDetailDO> taskDetails = robotTaskDetailMapper.queryByTaskId(robotTaskDetailDO.getRobotTaskId());
        boolean done =
                taskDetails.stream().noneMatch(v -> (v.getTaskStatus().equals(RobotTaskDetailStatusEnum.NEW.getType())));
        if (done) {
            RobotTaskDO robotTaskDO = new RobotTaskDO();
            robotTaskDO.setId(taskDetails.get(0).getRobotTaskId());
            robotTaskDO.setEndTime(LocalDateTime.now());
            robotTaskDO.setTaskStatus(RobotTaskDetailStatusEnum.DONE.getType());
            robotTaskMapper.updateRobot(robotTaskDO);
            taskCycleMapper.deletByRobotTaskId(taskDetails.get(0).getRobotTaskId());
        }

        if (RobotTaskTypeEnum.MOVE.getType().equals(robotTaskDetailDO.getTaskType())) {
            robotInformationMapper.updateRobotTaskModel(robotTaskDetailDO.getRobotNo(), RobotTaskModelEnum.REJECTION.getType());
        }
    }

    /**
     * 添加操作日志
     *
     * @param commandState
     */
    public void addActionLog(List<RobotCommandStateDTO> commandState, String robotDoingActionKey) {
        RobotCommandStateDTO robotCommandStateDTO = commandState.stream()
                .filter(v -> RobotExecutionStateConstant.DOING.equals(Integer.valueOf(v.getExecution_state())))
                .findFirst()
                .orElse(null);
        if (ObjectUtil.isNotEmpty(robotCommandStateDTO)) {
            String robotDoingActionLogKey = RobotTaskChcheConstant.ROBOT_ACTION_LOG_ENTITY + robotCommandStateDTO.getCommand_id();
            Object o = redisUtil.get(robotDoingActionLogKey);
            if (ObjectUtil.isNotEmpty(o)) {
                RobotTaskDetailActionLogDO actionLog = JSON.parseObject(JSON.toJSONString(o), RobotTaskDetailActionLogDO.class);
                taskDetailActionLogMapper.insert(actionLog);
                redisUtil.set(robotDoingActionKey, actionLog.getActionMsg(), doingActionCacheTime);
            }
        }
    }

    /**
     * 任务处理中
     *
     * @param robotCompleteTaskDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void taskDoing(RobotCompleteTaskDTO robotCompleteTaskDTO, String robotDoingActionKey) {
        List<RobotCommandStateDTO> commandState = robotCompleteTaskDTO.getCommand_state();
        if (ObjectUtil.isEmpty(commandState)) {
            return;
        }
        addActionLog(commandState, robotDoingActionKey);

        String robotActionTakeGoodsKey = RobotTaskChcheConstant.ROBOT_ACTION_TAKE_GOODS + robotCompleteTaskDTO.getOrder_id();
        Object o1 = redisUtil.get(robotActionTakeGoodsKey);

        String robotActionReleaseGoodsKey = RobotTaskChcheConstant.ROBOT_ACTION_RELEASE_GOODS + robotCompleteTaskDTO.getOrder_id();
        Object o2 = redisUtil.get(robotActionReleaseGoodsKey);

        if (ObjectUtil.isEmpty(o1) && ObjectUtil.isEmpty(o2)) {
            return;
        }

        String takeCommonId = ObjectUtil.isNotEmpty(o1) ? String.valueOf(o1) : null;
        String releaseCommonId = ObjectUtil.isNotEmpty(o2) ? String.valueOf(o2) : null;

        for (RobotCommandStateDTO commandStateDTO : commandState) {
            //释放取的库位
            if (RobotExecutionStateConstant.DONE.equals(Integer.valueOf(commandStateDTO.getExecution_state()))
                    && ObjectUtil.isNotEmpty(takeCommonId) && takeCommonId.equals(commandStateDTO.getCommand_id())) {
                RobotTaskDetailActionLogDO actionLogDO =
                        taskDetailActionLogMapper.selectOne(new LambdaQueryWrapperX<RobotTaskDetailActionLogDO>()
                                .eq(RobotTaskDetailActionLogDO::getCommandId, commandStateDTO.getCommand_id()));
                if (ObjectUtil.isNotEmpty(actionLogDO) && ObjectUtil.isNotEmpty(actionLogDO.getTaskDetailId())) {
                    freeLocation(true, actionLogDO.getTaskDetailId());
                    redisUtil.del(robotActionTakeGoodsKey);
                }
            }

            //释放放的库位
            if (RobotExecutionStateConstant.DONE.equals(Integer.valueOf(commandStateDTO.getExecution_state()))
                    && ObjectUtil.isNotEmpty(releaseCommonId) && releaseCommonId.equals(commandStateDTO.getCommand_id())) {
                RobotTaskDetailActionLogDO actionLogDO =
                        taskDetailActionLogMapper.selectOne(new LambdaQueryWrapperX<RobotTaskDetailActionLogDO>()
                                .eq(RobotTaskDetailActionLogDO::getCommandId, commandStateDTO.getCommand_id()));
                if (ObjectUtil.isNotEmpty(actionLogDO) && ObjectUtil.isNotEmpty(actionLogDO.getTaskDetailId())) {
                    freeLocation(false, actionLogDO.getTaskDetailId());
                    redisUtil.del(robotActionReleaseGoodsKey);
                }
            }
        }

    }

    public void freeLocation(Boolean isTake, Long taskDetailId) {
        RobotTaskDetailDO robotTaskDetailDO = robotTaskDetailMapper.selectById(taskDetailId);
        if (ObjectUtil.isNotEmpty(robotTaskDetailDO) && ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLocationId())
                && isTake) {
            WareHouseLocationDO wareHouseLocationDO = new WareHouseLocationDO();
            wareHouseLocationDO.setId(robotTaskDetailDO.getFromLocationId());
            wareHouseLocationDO.setLocationLock(LocationLockEnum.YES.getType());
            wareHouseLocationDO.setSkuBatch("");
            wareHouseLocationDO.setSkuInfo("");
            wareHouseLocationDO.setSkuNumber(0l);
            wareHouseLocationDO.setLocationUseStatus(LocationUseStatusEnum.NO.getType());
            locationMapper.updateEntity(wareHouseLocationDO);
        }

        if (ObjectUtil.isNotEmpty(robotTaskDetailDO) && ObjectUtil.isNotEmpty(robotTaskDetailDO.getToLocationId())
                && !isTake) {
            RobotTaskDO robotTask = robotTaskMapper.selectById(robotTaskDetailDO.getRobotTaskId());
            WareHouseLocationDO wareHouseLocationDO = new WareHouseLocationDO();
            wareHouseLocationDO.setId(robotTaskDetailDO.getFromLocationId());
            wareHouseLocationDO.setLocationLock(LocationLockEnum.YES.getType());
            wareHouseLocationDO.setSkuBatch(robotTask.getSkuBatch());
            wareHouseLocationDO.setSkuInfo(robotTask.getSkuInfo());
            wareHouseLocationDO.setSkuNumber(robotTask.getSkuNumber());
            wareHouseLocationDO.setLocationUseStatus(LocationUseStatusEnum.YES.getType());
            locationMapper.updateEntity(wareHouseLocationDO);
        }
    }

    /**
     * 机器人完成任务上报
     *
     * @param robotCompleteTaskDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> robotDoneTask(RobotCompleteTaskDTO robotCompleteTaskDTO) {
        taskExecutor.execute(() -> {
            doRobotDoneTask(robotCompleteTaskDTO);
        });

        return CommonResult.success(true);
    }
}
