package com.ltgk.boat.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.ltgk.boat.domain.dto.*;
import com.ltgk.boat.domain.entity.DsEnterOutPort;
import com.ltgk.boat.domain.entity.PointSolrEntity;
import com.ltgk.boat.domain.vo.AutomaticIdentificationVO;
import com.ltgk.boat.domain.vo.EnterAndOutVO;
import com.ltgk.boat.domain.vo.EnterOutPortStatisticsVO;
import com.ltgk.boat.domain.vo.NotReportBoatVO;
import com.ltgk.boat.mapper.DsEnterOutPortMapper;
import com.ltgk.boat.service.IDsEnterOutPortService;
import com.ltgk.common.common.SolrCommon;
import com.ltgk.common.constant.CacheConstants;
import com.ltgk.common.core.redis.RedisCache;
import com.ltgk.common.utils.DateUtils;
import com.ltgk.common.utils.GeoUtils;
import com.ltgk.common.utils.bean.BeanUtils;
import com.ltgk.port.domain.entity.DsMainArea;
import com.ltgk.port.service.IDsMainAreaService;
import com.ltgk.system.domain.vo.BoatNavigateStatus;
import com.ltgk.system.domain.vo.DsDynamicRadarVO;
import com.ltgk.system.service.DynamicDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.Field;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.ltgk.common.utils.PageUtils.startPage;


/**
 * 渔船进出港记录（数据来自船位动态监控）Service业务层处理
 *
 * @author Qi ChengBin
 * @date 2024-12-19
 */
@Service
public class DsEnterOutPortServiceImpl extends ServiceImpl<DsEnterOutPortMapper, DsEnterOutPort> implements IDsEnterOutPortService {
    @Autowired
    private DynamicDataService dynamicDataService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IDsMainAreaService dsMainAreaService;

    @Override
    public List<DsEnterOutPort> getEnterOutPortList(String boatName) {
        LambdaQueryWrapper<DsEnterOutPort> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DsEnterOutPort::getBoatName, boatName);
        lqw.orderByDesc(DsEnterOutPort::getEnterOutTime);
        return this.list(lqw);
    }

    @Override
    public List<BoatNavigateStatus> getWaitingArea(GetAreaBoatListReq getAreaBoatListReq) throws JsonProcessingException {
        // 获取待泊区 区域位置
        LambdaQueryWrapper<DsMainArea> lqw = new LambdaQueryWrapper<>();
        lqw.eq(DsMainArea::getId, getAreaBoatListReq.getMainAreaId());
        DsMainArea dsMainArea = dsMainAreaService.getOne(lqw);
        int regionType = 0;
        String region = "[{\"x\":108.212615,\"y\":21.51282},{\"x\":109.1228567,\"y\":21.51282},{\"x\":109.1228567,\"y\":19.076025},{\"x\":108.212615,\"y\":19.076025}]";
        if (dsMainArea.getAreaType().equals("Circle")) {
            regionType = 1;
            // 圆形数据组装经纬度
//            String[] split = dsMainArea.getPositionInfo().split(",");
//            if (split.length != 3){
//                return null;
//            }
//            region = "[{\"x\":" + split[0] + ",\"y\":" + split[1] + "}," + split[2] + "]";
        }
//        else {
        // 非圆形【四边形、多边形】 数据组装经纬度
//            String[] split = dsMainArea.getPositionInfo().split(";");
//            if(split.length < 4){
//                return null;
//            }
//            List<Point> points = new ArrayList<>();
//            for (int i = 0; i < split.length; i++) {
//                String[] split1 = split[i].split(",");
//                if (split1.length != 2){
//                    return null;
//                }
//                points.add(new Point(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])));
//            }
//            region = JSONObject.toJSONString(points);
//        }

        List<BoatNavigateStatus> list = new ArrayList<>();
        // 获取区域内渔船
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> dynamicList = dynamicDataService.getDynamicCountByRegion(region, 0);
        dynamicList.forEach(item -> {
            // 获取缓存的名字用来获取 缓存的信息
            String cacheName = item.getContent().getName();
            if (ObjectUtils.isNotEmpty(cacheName)) {
                Object cacheObject = redisCache.getCacheObject(CacheConstants.TERMINAL_KEY + "-" + cacheName);
                if (ObjectUtils.isNotEmpty(cacheObject)) {
                    DsDynamicRadarVO boatDynamicData = JSON.parseObject(cacheObject.toString(), DsDynamicRadarVO.class);
                    // 根据信息获取渔船数据
                    if (ObjectUtils.isNotEmpty(boatDynamicData)) {
                        BoatNavigateStatus boatNavigateStatus = new BoatNavigateStatus();
                        boatNavigateStatus.setBoatId(boatDynamicData.getBoatId());
                        boatNavigateStatus.setBoatName(boatDynamicData.getBoatName());
                        boatNavigateStatus.setDeviceType(boatDynamicData.getDeviceType());
                        boatNavigateStatus.setTerminalCode(StringUtils.isNotBlank(boatDynamicData.getMmsi()) ? boatDynamicData.getMmsi() : boatDynamicData.getTargetSourceId());
                        boatNavigateStatus.setLongitude(boatDynamicData.getLongitude());
                        boatNavigateStatus.setLatitude(boatDynamicData.getLatitude());
                        boatNavigateStatus.setSog(boatDynamicData.getSpeed());
                        boatNavigateStatus.setCog(boatDynamicData.getCourse());
                        boatNavigateStatus.setHeading(boatDynamicData.getCourse());
                        boatNavigateStatus.setGpsTime(boatDynamicData.getLocationTime());
                        boatNavigateStatus.setBoatLength(boatDynamicData.getBoatLength());
                        boatNavigateStatus.setMoldedBreadth(boatDynamicData.getBoatWidth());
                        list.add(boatNavigateStatus);
                    }
                }
            }
        });
        return list;
    }

    @Override
    public List<BoatNavigateStatus> getRegionBoat(GetRegionBoatReq getRegionBoatReq) throws JsonProcessingException {
        List<BoatNavigateStatus> list = new ArrayList<>();
        String region;
        if (getRegionBoatReq.getRegionType().equals(1)) {
            // 圆形数据组装经纬度
            String[] split = getRegionBoatReq.getRegion().split(",");
            if (split.length != 3) {
                throw new RuntimeException("参数错误");
            }
            region = "[{\"x\":" + split[0] + ",\"y\":" + split[1] + "}," + split[2] + "]";
        } else {
            // 非圆形【四边形、多边形】 数据组装经纬度
            String[] split = getRegionBoatReq.getRegion().split(";");
            if (split.length < 4) {
                throw new RuntimeException("参数错误");
            }
            List<Point> points = new ArrayList<>();
            for (String s : split) {
                String[] split1 = s.split(",");
                if (split1.length != 2) {
                    throw new RuntimeException("参数错误");
                }
                points.add(new Point(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])));
            }
            region = JSONObject.toJSONString(points);
        }

        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> dynamicList = dynamicDataService.getDynamicCountByRegion(region, getRegionBoatReq.getRegionType());
        dynamicList.forEach(item -> {
            // 获取缓存的名字用来获取 缓存的信息
            String cacheName = item.getContent().getName();
            if (ObjectUtils.isNotEmpty(cacheName)) {
                Object cacheObject = redisCache.getCacheObject(CacheConstants.TERMINAL_KEY + "-" + cacheName);
                if (ObjectUtils.isNotEmpty(cacheObject)) {
                    String boatInfo = cacheObject.toString();
                    if (ObjectUtils.isNotEmpty(boatInfo)) {
                        DsDynamicRadarVO boatDynamicData = JSON.parseObject(cacheObject.toString(), DsDynamicRadarVO.class);
                        // 根据信息获取渔船数据
                        if (ObjectUtils.isNotEmpty(boatDynamicData)) {
                            BoatNavigateStatus boatNavigateStatus = new BoatNavigateStatus();
                            boatNavigateStatus.setBoatId(boatDynamicData.getBoatId());
                            boatNavigateStatus.setBoatName(boatDynamicData.getBoatName());
                            boatNavigateStatus.setDeviceType(boatDynamicData.getDeviceType());
                            boatNavigateStatus.setTerminalCode(StringUtils.isNotBlank(boatDynamicData.getMmsi()) && !boatDynamicData.getMmsi().equals("0") ? boatDynamicData.getMmsi() : boatDynamicData.getTargetSourceId());
                            boatNavigateStatus.setLongitude(boatDynamicData.getLongitude());
                            boatNavigateStatus.setLatitude(boatDynamicData.getLatitude());
                            boatNavigateStatus.setSog(boatDynamicData.getSpeed());
                            boatNavigateStatus.setCog(boatDynamicData.getCourse());
                            boatNavigateStatus.setHeading(boatDynamicData.getCourse());
                            boatNavigateStatus.setGpsTime(boatDynamicData.getLocationTime());
                            boatNavigateStatus.setBoatLength(boatDynamicData.getBoatLength());
                            boatNavigateStatus.setMoldedBreadth(boatDynamicData.getBoatWidth());
                            list.add(boatNavigateStatus);
                        }
                    }
                }
            }
        });
        // 去除 AIS 和 雷达扫描的重复数据，保留AIS -- 经询问珏壮，在重复数据中，只保存AIS 的数据即可
        Map<String, List<BoatNavigateStatus>> boatNavigateStatusGroup = list.stream()
                .collect(Collectors.groupingBy(BoatNavigateStatus::getTerminalCode));
        return boatNavigateStatusGroup.values().stream()
                .flatMap(group -> {
                    if (group.size() == 1) {
                        return group.stream();
                    } else {
                        // 如果有重复数据，则保留AIS的数据
                        return group.stream().filter(item -> !"RADAR".equals(item.getDeviceType()));
                    }
                }).collect(Collectors.toList());
    }

    /**
     * 自动识别记录：包括AIS/北斗 设备解析和视频识别算法
     *
     * @param req： 自动识别记录请求参数
     * @return java.util.List<?>
     * @author Qi ChengBin
     * @date 2025/3/4
     */
    @Override
    public List<AutomaticIdentificationVO> findAutomaticIdentificationList(AutomaticIdentificationReq req) {
        startPage(req.getPageNum(), req.getPageSize());
        return baseMapper.findAutomaticIdentificationList(req);
    }

    @Override
    public List<BoatNavigateStatus> getRegionBoatPlayback(GetRegionBoatPlaybackReq getRegionBoatPlaybackReq) {
        Double x = null; // 圆心经度
        Double y = null; // 圆心纬度
        Double radius = null; // 半径 (米)
        // 需要先拆分 区域数据  获取经纬度范围
        // 如果是方形  或者是不规则
        List<Point> points = new ArrayList<>();
        if (getRegionBoatPlaybackReq.getRegionType().equals(0)) {
            String[] split = getRegionBoatPlaybackReq.getRegion().split(";");
            if (split.length >= 4) {
                for (int i = 0; i < split.length; i++) {
                    String[] split1 = split[i].split(",");
                    if (split1.length == 2) {
                        points.add(new Point(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])));
                    }
                }
            }
            // 获取最小的经度 最大的纬度坐标点  获取最大的经度 最小的纬度坐标点 获取一个矩形框的左上角 右下角左边 计算圆心以及半径
            DoubleSummaryStatistics xStatistics = points.stream().mapToDouble(Point::getX).summaryStatistics();
            DoubleSummaryStatistics yStatistics = points.stream().mapToDouble(Point::getY).summaryStatistics();
            GeoUtils.CoordinatePoint point1 = new GeoUtils.CoordinatePoint(new BigDecimal(xStatistics.getMin()), new BigDecimal(yStatistics.getMax()));
            GeoUtils.CoordinatePoint point2 = new GeoUtils.CoordinatePoint(new BigDecimal(xStatistics.getMax()), new BigDecimal(yStatistics.getMin()));
            // 根据坐标点计算距离
            GeoUtils.CenterAndDistance centerPoint = GeoUtils.calculateCenterAndDistance(point1, point2);
            y = centerPoint.getCenterPoint().getLatitude().doubleValue();
            x = centerPoint.getCenterPoint().getLongitude().doubleValue();
            radius = centerPoint.getDistance().doubleValue() * 1000;
        } else if (getRegionBoatPlaybackReq.getRegionType().equals(1)) { // 如果是圆形
            // 解析数据
            String[] split = getRegionBoatPlaybackReq.getRegion().split(",");
            if (split.length == 3) {
                x = Double.parseDouble(split[0]);
                y = Double.parseDouble(split[1]);
                radius = Double.parseDouble(split[2]);
            }

        }
        if (ObjectUtils.isEmpty(x) || ObjectUtils.isEmpty(y) || ObjectUtils.isEmpty(radius))
            return null;
        // 创建查询中心点
        Point center = new Point(y, x);
        // 创建距离对象
        Distance distance = new Distance(radius / 1000, Metrics.KILOMETERS);
        // 直接查询solr的轨迹数据
        Criteria criteria = new Criteria("locationTime")
                .between(DateUtils.toDate(getRegionBoatPlaybackReq.getStartTime()), DateUtils.toDate(getRegionBoatPlaybackReq.getEndTime()))
                .and("storeLocation").near(center, distance);
        if (StringUtils.isNotBlank(getRegionBoatPlaybackReq.getBoatName()))
            criteria.and("boatName").contains(getRegionBoatPlaybackReq.getBoatName());
        SimpleQuery query = new SimpleQuery(criteria);
        query.addSort(Sort.by(Sort.Direction.DESC, "locationTime"));
        query.addGroupByField(Field.of("mmsi"));
        query.setRows(100000);
        Page page = SolrCommon.solrSearchByQuery(CacheConstants.ltgkPoint, query, PointSolrEntity.class);
        ArrayList<BoatNavigateStatus> list = new ArrayList<>();
        page.getContent().forEach(item -> {
            PointSolrEntity pointSolrEntity = (PointSolrEntity) item;
            BoatNavigateStatus boatNavigateStatus = new BoatNavigateStatus();
            BeanUtils.copyBeanProp(boatNavigateStatus, pointSolrEntity);
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getLocationTime()))
                boatNavigateStatus.setGpsTime(pointSolrEntity.getLocationTime());
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getMmsi()))
                boatNavigateStatus.setTerminalCode(pointSolrEntity.getMmsi());
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getBoatLength()))
                boatNavigateStatus.setBoatLength(BigDecimal.valueOf(pointSolrEntity.getBoatLength()));
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getBoatWidth()))
                boatNavigateStatus.setMoldedBreadth(BigDecimal.valueOf(pointSolrEntity.getBoatWidth()));
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getLatitude()))
                boatNavigateStatus.setLatitude(pointSolrEntity.getLatitude());
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getLongitude()))
                boatNavigateStatus.setLongitude(pointSolrEntity.getLongitude());
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getCourse()))
                boatNavigateStatus.setCog(pointSolrEntity.getCourse());
            if (ObjectUtils.isNotEmpty(pointSolrEntity.getSpeed()))
                boatNavigateStatus.setSog(pointSolrEntity.getSpeed());
            list.add(boatNavigateStatus);
        });
        return list;
    }

    /**
     * 渔船统计管理 - 进出港统计分析
     * -- 接口权限：后台暂未设置权限
     *
     * @param dto: 渔船统计管理 - 进出港统计分析 请求参数
     * @return {@link EnterOutPortStatisticsVO}
     * @author Qi ChengBin
     * @date 2025/3/24
     */
    @Override
    public EnterOutPortStatisticsVO getEnterOutPortStatistics(EnterOutPortStatisticsDTO dto) {
        //开始时间的最开始
        Date beginOfDay = DateUtil.beginOfDay(DateUtil.parse(dto.getStartTime()));

        //结束时间的最后
        Date endOfDay = DateUtil.endOfDay(DateUtil.parse(dto.getEndTime()));

        // 获取时间段内的 进出港数据
        List<DsEnterOutPort> enterOutPortList = this.list(new LambdaQueryWrapper<DsEnterOutPort>()
                .between(DsEnterOutPort::getEnterOutTime, beginOfDay, endOfDay)
        );

        // 获取开始时间和结束时间所在天数
        List<String> dateList = DateUtil.rangeToList(beginOfDay, endOfDay, DateField.DAY_OF_MONTH)
                .stream()
                .map(date -> DateUtil.format(date, "yyyy-MM-dd"))
                .collect(Collectors.toList());

        // 按照时间进行分组
        Map<String, List<DsEnterOutPort>> enterOutPortMap = enterOutPortList.stream().collect(Collectors.groupingBy(dsEnterOutPort -> DateUtil.format(dsEnterOutPort.getEnterOutTime(), "yyyy-MM-dd")));

        // 进港数量列表
        List<Long> enterCountList = new ArrayList<>();
        // 出港数量列表
        List<Long> outCountList = new ArrayList<>();

        dateList.forEach(date -> {
            if (enterOutPortMap.containsKey(date)) {
                enterCountList.add(enterOutPortMap.get(date).stream().filter(dsEnterOutPort -> dsEnterOutPort.getEntryDir().equals(0)).count());
                outCountList.add(enterOutPortMap.get(date).stream().filter(dsEnterOutPort -> dsEnterOutPort.getEntryDir().equals(1)).count());
            } else {
                enterCountList.add(0L);
                outCountList.add(0L);
            }
        });

        // 根据 渔港进行分组，然后根据时间进行分组
        Map<String, Map<String, Long>> enterOutCountByPort = enterOutPortList.stream()
                .collect(Collectors.groupingBy(DsEnterOutPort::getPortName,
                        Collectors.groupingBy(a -> DateUtil.format(a.getEnterOutTime(), "yyyy-MM-dd"),
                                Collectors.counting()))
                );
        enterOutCountByPort.forEach((portName, dateMap) ->
                dateList.forEach(date -> dateMap.putIfAbsent(date, 0L))
        );
        // 将不同渔港分组中按照时间进行排序
        enterOutCountByPort.replaceAll((portName, dateCountMap) ->
                dateCountMap.entrySet().stream()
                        .sorted(Map.Entry.comparingByKey())
                        .collect(Collectors.toMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue,
                                (oldValue, newValue) -> newValue,
                                LinkedHashMap::new
                        ))
        );

        return new EnterOutPortStatisticsVO()
                // 时间列表
                .setDateList(dateList)
                // 进港数量
                .setEnterCountList(enterCountList)
                // 出港数量
                .setOutCountList(outCountList)
                // 进出港统计占比
                .setEnterOutProportion(
                        new EnterAndOutVO()
                                .setEnter(enterOutPortList.stream().filter(dsEnterOutPort -> dsEnterOutPort.getEntryDir().equals(0)).count())
                                .setOut(enterOutPortList.stream().filter(dsEnterOutPort -> dsEnterOutPort.getEntryDir().equals(1)).count())
                )
                // 港口船舶进出港数量分析
                .setEnterOutCountByPort(enterOutCountByPort);
    }

    /**
     * 查询未报告渔船管理 - 查询所有未报备的渔船，包括不在系统中管理的渔船（根据长海项目需求）
     *
     * @param req：未报告渔船管理请求参数
     * @return com.ltgk.common.core.page.TableDataInfo
     * @author Qi ChengBin
     * @date 2025/4/11
     */
    @Override
    public List<NotReportBoatVO> findNotReportBoatList(NotReportBoatReq req) {
        startPage(req.getPageNum(), req.getPageSize());
        return baseMapper.findNotReportBoatList(req);
    }
}
