package com.ltgk.boat.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ltgk.boat.domain.dto.*;
import com.ltgk.boat.domain.entity.DsBoatDevice;
import com.ltgk.boat.domain.entity.PointSolrEntity;
import com.ltgk.boat.domain.vo.BoatTrajectoryVo;
import com.ltgk.boat.domain.vo.DsDynamicWakeVo;
import com.ltgk.boat.domain.vo.ExportBoatTrajectoryVO;
import com.ltgk.boat.enums.BoatTrajectoryEnums;
import com.ltgk.boat.mapper.DsBoatDataMapper;
import com.ltgk.boat.service.BoatDynamicWakeService;
import com.ltgk.boat.service.IDsBoatDeviceService;
import com.ltgk.common.common.SolrCommon;
import com.ltgk.common.constant.CacheConstants;
import com.ltgk.common.core.domain.AjaxResult;
import com.ltgk.common.utils.DateUtils;
import com.ltgk.common.utils.GeoUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: wan kun
 * @Description: 获取船舶尾迹数据
 * @Date: 2025/2/26 11:22
 * @Version: 1.0
 */
@Log4j2
@Service
public class BoatDynamicWakeServiceImpl implements BoatDynamicWakeService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private DsBoatDataMapper dsBoatDataMapper;
    @Resource
    private IDsBoatDeviceService dsBoatDeviceService;

    @Override
    public AjaxResult getWakeData(GetWakeDataReq getWakeDataReq) {
        if (StringUtils.isBlank(getWakeDataReq.getBoatIds())) return AjaxResult.success();
        List<String> boatIds = Arrays.asList(getWakeDataReq.getBoatIds().split(","));
        // 因为 缓存的时候是boat_id + "_" + AIS + "_" + 设备号 前端传值时是 boat_id + "&" + 设备号 所以需要先转换
        HashMap<String, List<DsDynamicWakeVo>> wakeMap = new HashMap<>();
        boatIds.forEach(boarId -> {
            if (boarId.contains("&")) {
                String[] split = boarId.split("&");
                if (StringUtils.isBlank(split[0]))
                    split[0] = "null";
                String redisKey = split[0] + "_" + "AIS" + "_" + split[1];
                Map<Object, Object> entries = redisTemplate.opsForHash().entries(CacheConstants.dsDynamicWakeKey + redisKey);
                if (entries.size() > 0) {
                    ArrayList<DsDynamicWakeVo> wakeVos = new ArrayList<>();
                    entries.forEach((key, value) -> {
                        // key是存储的时间戳 校验 时间
                        Date time = new Date(Long.parseLong(key.toString()));
                        // 如果时间超过规定的时间直接删除掉
                        if (time.getTime() > System.currentTimeMillis() - CacheConstants.EXPIRED_TIME_FIFTEEN_MINUTE * 1000) {
                            // 获取相应的区域信息
                            DsDynamicWakeVo dsDynamicWakeVo = JSONObject.parseObject(value.toString(), DsDynamicWakeVo.class);
                            wakeVos.add(dsDynamicWakeVo);
                        } else {
                            redisTemplate.opsForHash().delete(CacheConstants.dsDynamicWakeKey + redisKey, key);
                        }
                    });
                    // 对wakeVos 按照时间进行排序
                    wakeVos.sort((o1, o2) -> {
                        // 降序
                        return o2.getTime().compareTo(o1.getTime());
                    });
                    wakeMap.put(boarId, wakeVos);
                }
            }
        });
        return AjaxResult.success(wakeMap);
    }

    @Override
    public AjaxResult getRadarWakeData(GetRadarWakeDataReq getRadarWakeDataReq) {
        if (StringUtils.isBlank(getRadarWakeDataReq.getTargetSourceIds())) return AjaxResult.success();
        List<String> radarIds = Arrays.asList(getRadarWakeDataReq.getTargetSourceIds().split(","));
        // 因为 缓存的时候是null + "_" + RADAR + "_" + 设备号 前端传值时是  设备号 所以需要先转换
        HashMap<String, List<DsDynamicWakeVo>> wakeMap = new HashMap<>();
        radarIds.forEach(radar -> {
            String redisKey = "null_RADAR_" + radar;
            Map<Object, Object> entries = redisTemplate.opsForHash().entries(CacheConstants.dsDynamicWakeKey + redisKey);
            if (entries.size() > 0) {
                ArrayList<DsDynamicWakeVo> wakeVos = new ArrayList<>();
                entries.forEach((key, value) -> {
                    // key是存储的时间戳 校验 时间
                    Date time = new Date(Long.parseLong(key.toString()));
                    // 如果时间超过规定的时间直接删除掉
                    if (time.getTime() > System.currentTimeMillis() - CacheConstants.EXPIRED_TIME_TWO_HOUR * 1000) {
                        // 获取相应的区域信息
                        DsDynamicWakeVo dsDynamicWakeVo = JSONObject.parseObject(value.toString(), DsDynamicWakeVo.class);
                        wakeVos.add(dsDynamicWakeVo);
                    } else {
                        redisTemplate.opsForHash().delete(CacheConstants.dsDynamicWakeKey + redisKey, key);
                    }
                });
                // 对wakeVos 按照时间进行排序
                wakeVos.sort((o1, o2) -> {
                    // 降序
                    return o2.getTime().compareTo(o1.getTime());
                });
                wakeMap.put(radar, wakeVos);
            }
        });
        return AjaxResult.success(wakeMap);
    }

    @Override
    public AjaxResult getBoatTrajectory(GetBoatTrajectoryReq getBoatTrajectoryReq) {
        if (getBoatTrajectoryReq.getDeviceType().equals("AIS_RADAR")) {
            getBoatTrajectoryReq.setDeviceType("AIS");
        }
        Page page = null;
        if (getBoatTrajectoryReq.getDeviceType().equals("RADAR")){
            page = getRadarTrajectoryFromSolr(getBoatTrajectoryReq);
        }else if (getBoatTrajectoryReq.getDeviceType().equals("AIS")){
            page = getBoatTrajectoryFromSolr(getBoatTrajectoryReq);
        }
        if(ObjectUtils.isNotEmpty(page)){
            log.info("轨迹回放从solr 中查询的数据条数{}", page.getContent().size());
            return AjaxResult.success(this.drawWakePoint(page));
        }else {
            return AjaxResult.success();
        }
    }

    /**
     * 从solr 中获取渔船轨迹数据
     *
     * @param getBoatTrajectoryReq:
     * @return org.springframework.data.domain.Page
     * @author Qi ChengBin
     * @date 2025/7/25
     */
    private static Page getBoatTrajectoryFromSolr(GetBoatTrajectoryReq getBoatTrajectoryReq) {

        // 直接查询solr的轨迹数据
        Criteria criteria = new Criteria("mmsi")
                .is(getBoatTrajectoryReq.getDeviceCode())
                .and("deviceType")
                .is(getBoatTrajectoryReq.getDeviceType())
                .and("locationTime")
                .between(DateUtils.toDate(getBoatTrajectoryReq.getStartTime()), DateUtils.toDate(getBoatTrajectoryReq.getEndTime()));
        SimpleQuery query = new SimpleQuery(criteria);
        query.addSort(Sort.by(Sort.Direction.DESC, "locationTime"));
        query.setRows(100000);
        Page page = SolrCommon.solrSearchByQuery(CacheConstants.ltgkPoint, query, PointSolrEntity.class);
        return page;
    }

    /**
     * 从雷达radar solr中查询轨迹数据
     * @param getBoatTrajectoryReq
     * @return
     */
    private static Page getRadarTrajectoryFromSolr(GetBoatTrajectoryReq getBoatTrajectoryReq){
        // 直接查询solr的轨迹数据
        Criteria criteria = new Criteria("targetSourceId")
                .is(getBoatTrajectoryReq.getDeviceCode())
                .and("deviceType")
                .is(getBoatTrajectoryReq.getDeviceType())
                .and("locationTime")
                .between(DateUtils.toDate(getBoatTrajectoryReq.getStartTime()), DateUtils.toDate(getBoatTrajectoryReq.getEndTime()));
        SimpleQuery query = new SimpleQuery(criteria);
        query.addSort(Sort.by(Sort.Direction.DESC, "locationTime"));
        query.setRows(100000);
        Page page = SolrCommon.solrSearchByQuery(CacheConstants.radarPoint, query, PointSolrEntity.class);
        return page;
    }

    @Override
    public AjaxResult getBoatInfoByCondition(GetBoatInfoByConditionReq getBoatInfoByConditionReq) {
        return AjaxResult.success(dsBoatDataMapper.getBoatInfoByCondition(getBoatInfoByConditionReq));
    }

    @Override
    public AjaxResult getBoatsTrajectory(GetBoatsTrajectoryReq getBoatsTrajectoryReq) {
        // 直接查询solr的轨迹数据
        Criteria criteria = new Criteria("mmsi")
                .in(getBoatsTrajectoryReq.getDevicesCode())
                .and("deviceType")
                .is(getBoatsTrajectoryReq.getDeviceType())
                .and("locationTime")
                .between(DateUtils.toDate(getBoatsTrajectoryReq.getStartTime()), DateUtils.toDate(getBoatsTrajectoryReq.getEndTime()));
        SimpleQuery query = new SimpleQuery(criteria);
        query.addSort(Sort.by(Sort.Direction.DESC, "locationTime"));
        query.setRows(100000);
        Page page = SolrCommon.solrSearchByQuery(CacheConstants.ltgkPoint, query, PointSolrEntity.class);
        HashMap<String, ArrayList<BoatTrajectoryVo>> map = new HashMap<>();
        page.getContent().forEach(item -> {
            BoatTrajectoryVo boatTrajectoryVo = new BoatTrajectoryVo();
            PointSolrEntity pointSolrEntity = (PointSolrEntity) item;
            boatTrajectoryVo.setLatitude(pointSolrEntity.getLatitude());
            boatTrajectoryVo.setLongitude(pointSolrEntity.getLongitude());
            boatTrajectoryVo.setTime(pointSolrEntity.getLocationTime());
            if (map.containsKey(pointSolrEntity.getMmsi())) {
                map.get(pointSolrEntity.getMmsi()).add(boatTrajectoryVo);
            } else {
                ArrayList<BoatTrajectoryVo> list = new ArrayList<>();
                list.add(boatTrajectoryVo);
                map.put(pointSolrEntity.getMmsi(), list);
            }
        });
        return AjaxResult.success(map);
    }

    /**
     * 查询导出渔船轨迹数据
     *
     * @param req：
     * @return java.util.List<com.ltgk.boat.domain.vo.ExportBoatTrajectoryVO>
     * @author Qi ChengBin
     * @date 2025/7/25
     */
    @Override
    public List<ExportBoatTrajectoryVO> exportBoatTrajectoryVO(GetBoatTrajectoryReq req) {

        // 抽稀后的渔船轨迹的数据
        List<BoatTrajectoryVo> boatTrajectoryVos = this.drawWakePoint(getBoatTrajectoryFromSolr(req));

        DsBoatDevice dsBoatDevice = dsBoatDeviceService.lambdaQuery()
                .eq(DsBoatDevice::getMmsi, req.getDeviceCode())
                .last("limit 1")
                .one();

        return boatTrajectoryVos.stream()
                .map(item -> {
                    ExportBoatTrajectoryVO exportBoatTrajectoryVO = new ExportBoatTrajectoryVO();
                    exportBoatTrajectoryVO.setBoatName(Objects.nonNull(dsBoatDevice) ? dsBoatDevice.getBoatName() : null);
                    exportBoatTrajectoryVO.setMmsi(req.getDeviceCode());
                    exportBoatTrajectoryVO.setLatitude(item.getLatitude());
                    exportBoatTrajectoryVO.setLongitude(item.getLongitude());
                    exportBoatTrajectoryVO.setTime(item.getTime());
                    return exportBoatTrajectoryVO;
                })
                .collect(Collectors.toList());
    }

    // 尾迹点位抽希方法
    public List<BoatTrajectoryVo> drawWakePoint(Page page) {
        List<BoatTrajectoryVo> list = new ArrayList<>();
        // 确定抽希比例
        Integer draw = BoatTrajectoryEnums.getDraw(Long.valueOf(page.getContent().size()));
        if (draw.equals(0)) { // 如果不需要抽希 直接遍历后返回
            page.getContent().forEach(item -> {
                BoatTrajectoryVo boatTrajectoryVo = new BoatTrajectoryVo();
                PointSolrEntity pointSolrEntity = (PointSolrEntity) item;
                boatTrajectoryVo.setLatitude(pointSolrEntity.getLatitude());
                boatTrajectoryVo.setLongitude(pointSolrEntity.getLongitude());
                boatTrajectoryVo.setTime(pointSolrEntity.getLocationTime());
                list.add(boatTrajectoryVo);
            });
            return list;
        }
        // 对比点 坐标 先获取第一个点位坐标
        GeoUtils.CoordinatePoint contrastPoint = null;
        // 对比点 上传时间  如果 时间超过10分钟 点位很近的也加入到列表中
        Date contrastDate = null;
        // 根据抽希比例进行抽希
        for (int i = 0; i < page.getContent().size(); i++) {
            BoatTrajectoryVo boatTrajectoryVo = new BoatTrajectoryVo();
            PointSolrEntity pointSolrEntity = (PointSolrEntity) page.getContent().get(i);
            boatTrajectoryVo.setLatitude(pointSolrEntity.getLatitude());
            boatTrajectoryVo.setLongitude(pointSolrEntity.getLongitude());
            boatTrajectoryVo.setTime(pointSolrEntity.getLocationTime());
            // 剩下的需要按照抽希比例抽希  先将该比例下的第一个点记录 然后比较 如果该点与上一个点距离大于2000米 则将该点加入列表 如果定位时间超过了20分钟也直接加入
            if (i % draw == 0) {
                list.add(boatTrajectoryVo);
                contrastPoint = new GeoUtils.CoordinatePoint(BigDecimal.valueOf(pointSolrEntity.getLongitude()), BigDecimal.valueOf(pointSolrEntity.getLatitude()));
                contrastDate = pointSolrEntity.getLocationTime();
            } else { // 剩下的点位与第一个点位比较
                // 被比较点位
                GeoUtils.CoordinatePoint comparedPoint = new GeoUtils.CoordinatePoint(BigDecimal.valueOf(pointSolrEntity.getLongitude()), BigDecimal.valueOf(pointSolrEntity.getLatitude()));
                if (contrastDate.getTime() - pointSolrEntity.getLocationTime().getTime() > 20 * 60 * 1000) {
                    list.add(boatTrajectoryVo);
                    contrastPoint = comparedPoint;// 加入后 直接改变对比点坐标 以方便下次继续对比
                } else if (GeoUtils.calculateDistance(contrastPoint, comparedPoint) * 1000 > 2000) {
                    list.add(boatTrajectoryVo);
                    contrastPoint = comparedPoint;// 加入后 直接改变对比点坐标 以方便下次继续对比
                }
            }
        }
        log.info("轨迹回放抽希后的数据条数{}", list.size());
        return list;
    }
}
