package com.ruoyi.iot.service.impl;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ruoyi.bst.area.domain.AreaVO;
import com.ruoyi.bst.area.domain.vo.LocationAreaVO;
import com.ruoyi.bst.area.service.AreaService;
import com.ruoyi.bst.area.utils.AreaUtil;
import com.ruoyi.bst.areaSub.domain.AreaSubVO;
import com.ruoyi.bst.areaSub.service.AreaSubService;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.enums.DeviceLockStatus;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.device.domain.enums.DeviceUnLockType;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.device.utils.DeviceUtil;
import com.ruoyi.bst.locationLog.domain.LocationLog;
import com.ruoyi.bst.locationLog.service.LocationLogConverter;
import com.ruoyi.bst.orderDevice.domain.enums.OrderDeviceStatus;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.iot.constants.IotConstants;
import com.ruoyi.iot.domain.IotDeviceSysInfo;
import com.ruoyi.iot.domain.ReceiveMsg;
import com.ruoyi.iot.enums.ReceiveType;
import com.ruoyi.iot.service.IotReceiveService;
import com.ruoyi.iot.util.IotUtil;
import com.ruoyi.ws.service.DeviceWebSocketService;

import lombok.extern.slf4j.Slf4j;

/**
 * @author wjh
 * 2024/5/6
 */
@Service
@Slf4j
public class IotReceiveServiceImpl implements IotReceiveService {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private LocationLogConverter locationLogConverter;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private AreaSubService areaSubService;

    @Autowired
    private AreaService areaService;

    @Autowired
    private DeviceIotService deviceIotService;

    @Autowired
    private DeviceWebSocketService deviceWebSocketService;

    @Override
    public void handleReceive(ReceiveMsg msg) {
        if (msg == null) {
            return;
        }
        // 数据点推送
        if (ReceiveType.DATA_POINT.getType().equals(msg.getType())) {
            if (IotConstants.DS_SYS.equals(msg.getDsId())) {
                // 查询设备信息
                DeviceVO device = deviceService.selectSimpleByMacForLocationLog(msg.getDevName());
                if (device == null) {
                    return;
                }

                // 转为设备信息
                IotDeviceSysInfo sys = IotUtil.toSysInfo(msg.getValue());
                LocalDateTime at = DateUtils.toLocalDateTime(msg.getAt());

                // 处理设备定位BUG，若出现BUG则重启设备
                int handle = this.handleDeviceLocationBug(device, sys, at);
                if (handle == 1) {
                    return;
                }

                // 设置设备信息
                DeviceUtil.setIotSysInfo(device, sys);
                device.setLastTime(at);
                device.setLastLocationTime(at);

                // 卫星数量小于5，不处理设备操作
                if (device.getSatellites() == null || device.getSatellites() < 5) {
                    log.error("卫星数量小于5，不处理: {}", device.getMac());
                    return;
                }

                this.handleArea(device);

                this.handleLocationLog(device);
            }
        }
        // 生命周期（暂时不做，需要测试）
        else if (ReceiveType.DEVICE_STATUS.getType().equals(msg.getType())) {
            // this.handleReboot(msg);
        }
    }

    /**
     * 处理设备重启
     */
    private void handleReboot(ReceiveMsg msg) {
        if (msg == null || StringUtils.isBlank(msg.getDevName())) {
            return;
        }
        // 同步一次设备状态
        deviceIotService.monitor(msg.getDevName());
    }

    /**
     * 处理设备定位BUG
     * @param device
     */
    private int handleDeviceLocationBug(DeviceVO device, IotDeviceSysInfo sys, LocalDateTime at) {
        if (device == null || StringUtils.isBlank(device.getMac()) || device.getLastLocationTime() == null || at == null) {
            return 0;
        }
        boolean isOpen =  DeviceLockStatus.OPEN.getCode().equals(device.getLockStatus());   // 是否开锁
        // 需要在设备未启动的时候做，否则可能有安全问题
        if (!isOpen) {
            // 若当前数据点的上报时间和设备内上次上报的时间不一样，但是定位一样，则重启设备
            if (at.isAfter(device.getLastLocationTime()) && MathUtils.equals(device.getLongitude(), sys.getLon()) && MathUtils.equals(device.getLatitude(), sys.getLat())) {
                deviceIotService.reboot(device, "设备不同时间的两次定位一样，重启设备", true);
                return 1;
            }
        }    
        
        return 0;
    }

    /**
     * 处理车辆运营区
     */
    private void handleArea(DeviceVO device) {
        if (device == null || StringUtils.isBlank(device.getMac())) {
            return;
        }
        //  相差一分钟以上的消息不做处理
        Duration duration = Duration.between(device.getLastTime(), LocalDateTime.now());
        if (duration.getSeconds() > 60) {
            return;
        }
        // 若处理过，则不处理
        String key = CacheConstants.DEVICE_AREA_HANDLE_KEY + device.getMac();
        Integer isHandle = redisCache.getCacheObject(key);
        if (isHandle != null && isHandle == 1) {
            return;
        }

        // 管理员开锁，不处理
        boolean isAdminUnlocking = DeviceStatus.DISPATCHING.getCode().equals(device.getStatus());
        if (isAdminUnlocking) {
            return;
        }

        // 查询运营区
        AreaVO area = areaService.selectAreaById(device.getAreaId());
        if(area == null){
            return;
        }
        // 查询运营区内的禁行区
        List<AreaSubVO> noRidingList = areaSubService.selectNoRidingListByAreaId(device.getAreaId());

        // 处理设备在运营区内的行为
        int handle = handleDeviceArea(device, area, noRidingList);
        // 若处理了，则返回1，并设置缓存5秒
        if (handle == 1) {
            redisCache.setCacheObject(key, handle, 5, TimeUnit.SECONDS);
        }

    }

    // 设备运营区处理
    private int handleDeviceArea(DeviceVO device, AreaVO area, List<AreaSubVO> noRidingList) {
        if (device == null || area == null) {
            return 0;
        }

        boolean isOpen =  DeviceLockStatus.OPEN.getCode().equals(device.getLockStatus());   // 是否开锁
        boolean isQLocked = DeviceStatus.Q_LOCKED.getCode().equals(device.getStatus()); // 是否强制断电
        boolean isDispatching = DeviceStatus.DISPATCHING.getCode().equals(device.getStatus()); // 是否调度中

        // 是否有正在使用的订单
        boolean hasOrder = device.getOrderDeviceId() != null && OrderDeviceStatus.USING.getCode().equals(device.getOrderDeviceStatus());

        // 获取车辆位置信息
        LocationAreaVO locationArea = AreaUtil.getLocationArea(device.getLongitude(), device.getLatitude(), area, noRidingList);
        boolean isInArea = locationArea.getIsInArea() != null && locationArea.getIsInArea();
        boolean isInAreaMin = locationArea.getIsInAreaMin() != null && locationArea.getIsInAreaMin();
        boolean isInAreaMax = locationArea.getIsInAreaMax() != null && locationArea.getIsInAreaMax();
        boolean isInNoRidingArea = locationArea.getIsInNoRidingArea() != null && locationArea.getIsInNoRidingArea();
        boolean isNearNoRidingArea = locationArea.getIsNearNoRidingArea() != null && locationArea.getIsNearNoRidingArea();
        boolean hasAreaBoundary = StringUtils.isNotBlank(area.getBoundary());   // 是否有运营区边界

        // 在运营区内，并且不在禁行区内，并且车辆为强制断电状态，为车辆上电
        if (isInAreaMax && !isInNoRidingArea && isQLocked && !isOpen && hasOrder) {
            deviceIotService.unlock(device, DeviceUnLockType.BACK_AREA, "重新返回运营区上电", false);
            return 1;
        }

        boolean noRidingOutage = area.getNoRidingOutage() != null && area.getNoRidingOutage(); // 禁行区内断电
        if (noRidingOutage) {
            // 进入禁行区，并且车辆为开锁状态，强制断电
            if (isInNoRidingArea && isOpen) {
                deviceIotService.qLock(device, "进入禁行区，强制断电", false);
                return 1;
            }

            // 靠近禁行区，并且为开启状态，播报语音警告
            if (isNearNoRidingArea && isOpen) {
                deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_NEAR, "靠近禁行区", false);
                return 1;
            }
        }

        // 超出运营区域外的最大范围，且车辆为开锁状态，强制断电
        boolean areaOutOutage = area.getAreaOutOutage() != null && area.getAreaOutOutage(); // 运营区外断电
        if (areaOutOutage && hasAreaBoundary) {
            // 超出运营区最大边界 && 有订单 && 不是调度中，强制断电
            if (!isInAreaMax && hasOrder && !isDispatching) {
                deviceIotService.qLock(device, "超出运营区外边界最大值，强制断电", false);
                return 1;
            }
            // 二次警告
            if(!isInArea && isInAreaMax) {
                deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_OUT, "超出运营区外边界，还未到达断电距离", false);
                return 1;
            }
            // 一次警告
            if (isInArea && !isInAreaMin) {
                deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_NEAR, "靠近运营区内边界", false);
                return 1;
            }
        }

        return 0;
    }

    // 定位日志处理
    private void handleLocationLog(DeviceVO device) {
        // 转换定位日志
        LocationLog po = locationLogConverter.toPo(device);
        if (po == null) {
            log.error("转换定位日志失败: {}", device.getMac());
            return;
        }

        // 暂存到Redis缓存
        redisCache.rightPush(CacheConstants.LOCATION_LOG_QUEUE, po);

        // 发送消息给ws服务
        deviceWebSocketService.sendMessageToDevice(po, device.getMac());
    }
}
