package com.ruoyi.iot.service.impl;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

import org.locationtech.jts.geom.Geometry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ruoyi.bst.area.domain.AreaVO;
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.areaSub.utils.AreaSubUtil;
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.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.map.GeoUtils;
import com.ruoyi.common.utils.map.GpsCoordinateUtils;
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.service.IotService;
import com.ruoyi.iot.util.IotUtil;

import lombok.extern.slf4j.Slf4j;

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

    @Autowired
    private IotService iotService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private LocationLogConverter locationLogConverter;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private AreaSubService areaSubService;

    @Autowired
    private AreaService areaService;

    @Autowired
    private DeviceIotService deviceIotService;

    @Override
    public void handleReceive(ReceiveMsg msg) {
        if (msg == null) {
            return;
        }
        // 数据点推送
        if (ReceiveType.DATA_POINT.getType().equals(msg.getType())) {
            log.info("dsId = {}", msg.getDsId());
            if (IotConstants.DS_SYS.equals(msg.getDsId())) {
                // 查询设备信息
                DeviceVO device = deviceService.selectSimpleByMacForLocationLog(msg.getDevName());
                if (device == null) {
                    return;
                }
                // 转为设备信息
                IotDeviceSysInfo sys = IotUtil.toSysInfo(msg.getValue());
                // 设置设备信息
                DeviceUtil.setIotSysInfo(device, sys);
                LocalDateTime at = DateUtils.toLocalDateTime(msg.getAt());
                device.setLastTime(at);
                device.setLastLocationTime(at);

                this.handleArea(device);

                this.handleLocationLog(device);
            }
        }
        // 生命周期
        else if (ReceiveType.DEVICE_STATUS.getType().equals(msg.getType())) {
        }
    }

    /**
     * 处理车辆运营区
     */
    private void handleArea(DeviceVO device) {
        if (device == null) {
            return;
        }
        //  相差一分钟以上的消息不做处理
        Duration duration = Duration.between(device.getLastTime(), LocalDateTime.now());
        if (duration.getSeconds() > 60) {
            return;
        }
        // 管理员开锁，不处理
        boolean isAdminUnlocking = DeviceStatus.DISPATCHING.getCode().equals(device.getStatus());
        if (isAdminUnlocking) {
            return;
        }

        // 坐标转换  WGS84 转 GCJ02
        List<BigDecimal> position = GpsCoordinateUtils.coordinateConvert(device.getLongitude(), device.getLatitude());
        BigDecimal lon = position.get(1);
        BigDecimal lat = position.get(0);
        log.info("转换后的GCJ02经纬度：{}---{}", lon, lat);

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

        // 处理设备在运营区内的行为
        handleDeviceArea(device, area, noRidingList);

    }

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

        // 车辆是否开锁
        boolean isOpen =  DeviceLockStatus.OPEN.getCode().equals(device.getLockStatus());
        boolean isQLocked = DeviceStatus.Q_LOCKED.getCode().equals(device.getStatus());

        BigDecimal boundaryDistance = area.getBoundaryDistance();// 靠近运营区边界时的播报距离
        BigDecimal outAreaDistance = area.getOutageDistance();// 超出运营区外断电距离

        // 创建多边形对象
        Geometry polygon = GeoUtils.fromWkt(area.getBoundary());
        // 是否在运营区最小内边界
        boolean isInAreaMin = AreaUtil.isInAreaMin(area, device.getLongitude(), device.getLatitude());
        // 是否在运营区最大外边界
        boolean isInAreaMax = AreaUtil.isInAreaMax(area, device.getLongitude(), device.getLatitude());
        // 是否在运营区内
        boolean isInArea = AreaUtil.isInArea(area, device.getLongitude(), device.getLatitude());
        // 是否在禁行区内
        AreaSubVO inAreaSub = AreaSubUtil.getInAreaSub(noRidingList, device.getLongitude(), device.getLatitude(), area.getError());
        boolean isInNoRidingArea = inAreaSub != null;
        // 是否靠近禁行区
        AreaSubVO nearAreaSub = AreaSubUtil.getNearAreaSub(noRidingList, device.getLongitude(), device.getLatitude(), boundaryDistance);
        boolean isNearyNoRidingArea = nearAreaSub != null;
        
        boolean hasOrder = device.getOrderDeviceId() != null && OrderDeviceStatus.USING.getCode().equals(device.getOrderDeviceStatus());

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

        // 靠近运营区内边界，且车辆为开锁状态，播报语音警告
        if(isOpen) {
            if (isNearyNoRidingArea) {
                deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_NEAR, "靠近禁行区");
                return;
            }
            if (isInArea && !isInAreaMin) {
                deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_NEAR, "靠近运营区内边界");
                return;
            }
            
            // 靠近运营区外边界，且车辆为开锁状态，播报二次语音警告
            if(!isInArea && isInAreaMax) {
                deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_OUT, "超出运营区外边界，还未到达断电距离");
                return;
            } 

            // 超出运营区域外的最大范围，且车辆为开锁状态，强制断电
            if (area.getAreaOutOutage() != null && area.getAreaOutOutage()) {
                if (isInNoRidingArea) {
                    deviceIotService.qLock(device, false, "进入禁行区，强制断电", false);
                    return;
                }
                if (!isInAreaMax) {
                    deviceIotService.qLock(device, false, "超出运营区外边界最大值，强制断电", false);
                    return;
                }
            }
        } 
    
    }

    // 定位日志处理
    private void handleLocationLog(DeviceVO device) {
        if (device.getSatellites() == null || device.getSatellites() < 5) {
            log.error("卫星数量小于5，不处理定位日志: {}", device.getMac());
            return;
        }
        
        // 转换定位日志
        LocationLog po = locationLogConverter.toPo(device);
        if (po == null) {
            log.error("转换定位日志失败: {}", device.getMac());
            return;
        }

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