package com.fuyuanshen.global.mqtt.rule.bjq;

import com.alibaba.fastjson2.JSONObject;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.service.DeviceService;
import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil;
import com.fuyuanshen.equipment.utils.map.LngLonUtil;
import com.fuyuanshen.global.mqtt.base.MqttMessageRule;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.config.MqttGateway;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.mqtt.constants.LightingCommandTypeConstants;
import com.fuyuanshen.global.mqtt.constants.MqttConstants;
import com.fuyuanshen.global.mqtt.listener.domain.FunctionAccessStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import static com.fuyuanshen.common.core.constant.GlobalConstants.FUNCTION_ACCESS_KEY;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_LOCATION_KEY_PREFIX;

/**
 * 定位数据命令处理
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class BjqLocationDataRule implements MqttMessageRule {

    private final MqttGateway mqttGateway;
    private final DeviceService deviceService;


    @Override
    public String getCommandType() {
        return LightingCommandTypeConstants.LOCATION_DATA;
    }

    @Override
    public void execute(MqttRuleContext context) {
        String functionAccess = FUNCTION_ACCESS_KEY + context.getDeviceImei();
        try {
            Object[] convertArr = context.getConvertArr();
            // Latitude, longitude
            String latitude = convertArr[1].toString();
            String longitude = convertArr[2].toString();
            // 判断 latitude 和 longitude 是否都为 0
            if ("0".equals(latitude) && "0".equals(longitude)) {
                log.info("位置信息为0，不存储到Redis: device={}, lat={}, lon={}", context.getDeviceImei(), latitude, longitude);
                return;
            }
            // 异步发送经纬度到Redis
            asyncSendLocationToRedisWithFuture(context.getDeviceImei(), latitude, longitude);
            // 异步保存数据
            asyncSaveLocationToMySQLWithFuture(context.getDeviceImei(), latitude, longitude);

            Map<String, Object> map = buildLocationDataMap(latitude, longitude);
            mqttGateway.sendMsgToMqtt(MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(), 1, JsonUtils.toJsonString(map));
            log.info("发送定位数据到设备=>topic:{},payload:{}",
                    MqttConstants.GLOBAL_PUB_KEY + context.getDeviceImei(),
                    JsonUtils.toJsonString(map));
            RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.OK.getCode(), Duration.ofSeconds(20));
        } catch (Exception e) {
            log.error("处理定位数据命令时出错", e);
            RedisUtils.setCacheObject(functionAccess, FunctionAccessStatus.FAILED.getCode(), Duration.ofSeconds(20));
        }
    }

    /**
     * 异步发送位置信息到Redis
     *
     * @param deviceImei 设备IMEI
     * @param latitude 纬度
     * @param longitude 经度
     * @param timestamp 时间戳
     */
//    @Async
//    public void asyncSendLocationToRedis(String deviceImei, String latitude, String longitude, long timestamp) {
//        try {
//            // 构造位置信息对象
//            Map<String, Object> locationInfo = new HashMap<>();
//            locationInfo.put("deviceImei", deviceImei);
//            locationInfo.put("latitude", latitude);
//            locationInfo.put("longitude", longitude);
//            locationInfo.put("timestamp", timestamp);
//
//            // 将位置信息存储到Redis中，使用设备IMEI作为key的一部分
//            String redisKey = "device:location:" + deviceImei;
//            String locationJson = JsonUtils.toJsonString(locationInfo);
//
//            // 存储到Redis，设置过期时间（例如24小时）
//            RedisUtils.setCacheObject(redisKey, locationJson, Duration.ofSeconds(24 * 60 * 60));
//
//            // 也可以存储到一个列表中，保留历史位置信息
//            String locationHistoryKey = "device:location:history:" + deviceImei;
//            RedisUtils.lPush(locationHistoryKey, locationJson, 24 * 60 * 60);
//
//            log.info("位置信息已异步发送到Redis: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
//        } catch (Exception e) {
//            log.error("异步发送位置信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
//        }
//    }

    /**
     * 异步发送位置信息到Redis（使用CompletableFuture）
     *
     * @param deviceImei 设备IMEI
     * @param latitude   纬度
     * @param longitude  经度
     */
    public void asyncSendLocationToRedisWithFuture(String deviceImei, String latitude, String longitude) {
        try {
            if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
                return;
            }

//                String[] latArr = latitude.split("\\.");
//                String[] lonArr = longitude.split("\\.");
//                // 将位置信息存储到Redis中
            String redisKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DEVICE_LOCATION_KEY_PREFIX;
//                String redisObj = RedisUtils.getCacheObject(redisKey);
//                JSONObject jsonOBj = JSONObject.parseObject(redisObj);
//                if(jsonOBj != null){
//                    String str1 = latArr[0] +"."+ latArr[1].substring(0,4);
//                    String str2 = lonArr[0] +"."+ lonArr[1].substring(0,4);
//
//                    String cacheLatitude = jsonOBj.getString("wgs84_latitude");
//                    String cacheLongitude = jsonOBj.getString("wgs84_longitude");
//                    String[] latArr1 = cacheLatitude.split("\\.");
//                    String[] lonArr1 = cacheLongitude.split("\\.");
//
//                    String cacheStr1 = latArr1[0] +"."+ latArr1[1].substring(0,4);
//                    String cacheStr2 = lonArr1[0] +"."+ lonArr1[1].substring(0,4);
//                    if(str1.equals(cacheStr1) && str2.equals(cacheStr2)){
//                        log.info("位置信息未发生变化: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
//                        return;
//                    }
//                }

            // 构造位置信息对象
            Map<String, Object> locationInfo = new LinkedHashMap<>();
            double[] doubles = LngLonUtil.gps84_To_Gcj02(Double.parseDouble(latitude), Double.parseDouble(longitude));
            locationInfo.put("deviceImei", deviceImei);
            locationInfo.put("latitude", doubles[0]);
            locationInfo.put("longitude", doubles[1]);
            locationInfo.put("wgs84_latitude", latitude);
            locationInfo.put("wgs84_longitude", longitude);
            String address = GetAddressFromLatUtil.getAdd(String.valueOf(doubles[1]), String.valueOf(doubles[0]));
            locationInfo.put("address", address);
            locationInfo.put("timestamp", System.currentTimeMillis());


            String locationJson = JsonUtils.toJsonString(locationInfo);

            // 存储到Redis
            RedisUtils.setCacheObject(redisKey, locationJson);

            // 存储到一个列表中，保留历史位置信息
//                String locationHistoryKey = GlobalConstants.GLOBAL_REDIS_KEY+DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX + deviceImei;
//                RedisUtils.addCacheList(locationHistoryKey, locationJson);
//                RedisUtils.expire(locationHistoryKey, Duration.ofDays(90));
            storeDeviceTrajectoryWithSortedSet(deviceImei, locationJson);
            log.info("位置信息已异步发送到Redis: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
        } catch (Exception e) {
            log.error("异步发送位置信息到Redis时出错: device={}, error={}", deviceImei, e.getMessage(), e);
        }
    }


    /**
     * 异步保存位置信息到MySQL数据库
     *
     * @param deviceImei 设备IMEI
     * @param latitude   纬度
     * @param longitude  经度
     */
    public void asyncSaveLocationToMySQLWithFuture(String deviceImei, String latitude, String longitude) {
        try {
            if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
                return;
            }

            // 调用服务层方法更新设备位置信息
            deviceService.updateDeviceLocationByImei(deviceImei, longitude, latitude);

            log.info("位置信息已异步保存到MySQL: device={}, lat={}, lon={}", deviceImei, latitude, longitude);
        } catch (Exception e) {
            log.error("异步保存位置信息到MySQL时出错: device={}, error={}", deviceImei, e.getMessage(), e);
        }
    }


    /**
     * 存储设备30天历史轨迹到Redis (使用Sorted Set)
     */
    public void storeDeviceTrajectoryWithSortedSet(String deviceImei, String locationJson) {
        try {
            String trajectoryKey = GlobalConstants.GLOBAL_REDIS_KEY + DEVICE_KEY_PREFIX + deviceImei + DeviceRedisKeyConstants.DEVICE_LOCATION_HISTORY_KEY_PREFIX;
//            String trajectoryKey = "device:trajectory:zset:" + deviceImei;
//            String locationJson = JsonUtils.toJsonString(locationInfo);
            long timestamp = System.currentTimeMillis();

            // 添加到Sorted Set，使用时间戳作为score
            RedisUtils.zAdd(trajectoryKey, locationJson, timestamp);

//            // 设置30天过期时间
//            RedisUtils.expire(trajectoryKey, Duration.ofDays(30));

            // 清理30天前的数据（冗余保护）
            long thirtyDaysAgo = System.currentTimeMillis() - (7L * 24 * 60 * 60 * 1000);
            RedisUtils.zRemoveRangeByScore(trajectoryKey, 0, thirtyDaysAgo);
        } catch (Exception e) {
            log.error("存储设备轨迹到Redis(ZSet)失败: device={}, error={}", deviceImei, e.getMessage(), e);
        }
    }

    private Map<String, Object> buildLocationDataMap(String latitude, String longitude) {
        String[] latArr = latitude.split("\\.");
        String[] lonArr = longitude.split("\\.");

        ArrayList<Integer> intData = new ArrayList<>();
        intData.add(11);
        intData.add(Integer.parseInt(latArr[0]));
        String str1 = latArr[1];
        intData.add(Integer.parseInt(str1.substring(0, 4)));
        String str2 = lonArr[1];
        intData.add(Integer.parseInt(lonArr[0]));
        intData.add(Integer.parseInt(str2.substring(0, 4)));

        Map<String, Object> map = new HashMap<>();
        map.put("instruct", intData);
        return map;
    }
}
