package com.ltgk.boat.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ltgk.boat.domain.dto.*;
import com.ltgk.boat.domain.entity.*;
import com.ltgk.boat.domain.vo.*;
import com.ltgk.boat.mapper.DsBoatCredentialMapper;
import com.ltgk.boat.mapper.DsBoatDataMapper;
import com.ltgk.boat.mapper.DsBoatIllegalMapper;
import com.ltgk.boat.mapper.DsBoatInsuranceMapper;
import com.ltgk.boat.service.*;
import com.ltgk.common.common.SolrCommon;
import com.ltgk.common.constant.CacheConstants;
import com.ltgk.common.constant.Constants;
import com.ltgk.common.constant.ReturnMsgConstants;
import com.ltgk.common.core.domain.AjaxResult;
import com.ltgk.common.core.domain.entity.SysRole;
import com.ltgk.common.core.domain.entity.SysUser;
import com.ltgk.common.core.domain.model.LoginUser;
import com.ltgk.common.enums.PersonalDataEnums;
import com.ltgk.common.utils.DateUtils;
import com.ltgk.common.utils.GeoUtils;
import com.ltgk.common.utils.SecurityUtils;
import com.ltgk.common.utils.bean.BeanUtils;
import com.ltgk.common.utils.poi.ExcelUtil;
import com.ltgk.port.domain.dto.GetOutPortListReq;
import com.ltgk.port.domain.entity.DsAnnualInspection;
import com.ltgk.port.domain.entity.DsPort;
import com.ltgk.port.service.DsAnnualInspectionService;
import com.ltgk.port.service.IDsPortService;
import com.ltgk.port.service.PortStatisticsService;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
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 org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

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

/**
 * @Author: wan kun
 * @Description: 渔船实现类
 * @Date: 2024/10/25 19:19
 * @Version: 1.0
 */
@Service
public class DsBoatDataServiceImpl extends ServiceImpl<DsBoatDataMapper, DsBoatData> implements DsBoatDataService {

    @Resource
    private IDsBoatBindCrewService dsBoatBindCrewService;
    @Resource
    private DsBoatCredentialMapper dsBoatCredentialMapper;
    @Resource
    private DsBoatInsuranceMapper dsBoatInsuranceMapper;
    @Resource
    private DsBoatIllegalMapper dsBoatIllegalMapper;
    @Resource
    private DsAnnualInspectionService dsAnnualInspectionService;
    @Resource
    private DsBoatNameService dsBoatNameService;
    @Resource
    private DsBoatFishingService dsBoatFishingService;
    @Resource
    private DsBoatNationalityService dsBoatNationalityService;
    @Resource
    private IDsEnterOutPortService dsEnterOutPortService;
    @Resource
    private IDsBoatDeviceService dsBoatDeviceService;
    @Resource
    private BoatAlarmInfoService boatAlarmInfoService;
    @Resource
    private PortStatisticsService portStatisticsService;
    @Resource
    private IDsPortService dsPortService;

    @Override
    public List<DsBoatData> getBoatList(GetBoatListReq getBoatListReq) throws Throwable {
        // 根据登录用户的角色判断是否是查询个人数据权限
        SysUser user = SecurityUtils.getLoginUser().getUser();
        List<SysRole> loginUserRoleList = user.getRoles();

        if (loginUserRoleList.stream().anyMatch(item -> item.getRoleKey().equals(PersonalDataEnums.PERSONAL_DATA.getValue()))) {
            // 查询渔船实际经营人
            getBoatListReq.setOperatorUserId(String.valueOf(user.getUserId()));
        }
        // 框架分页有bug 只会对第一次list分页有效 所以必须先执行一次
        startPage(getBoatListReq.getPageNum(), getBoatListReq.getPageSize());
        List<DsBoatData> dsBoatData = baseMapper.selectByPage(getBoatListReq);
        if (CollectionUtils.isNotEmpty(dsBoatData)) {
            List<String> boatIdList = dsBoatData.stream().map(DsBoatData::getBoatId).collect(Collectors.toList());

            List<DsBoatDevice> dbsBoatDeviceList = dsBoatDeviceService
                    .lambdaQuery()
                    .in(DsBoatDevice::getBoatId, boatIdList)
                    .eq(DsBoatDevice::getDelFlag, Constants.FLAG_FALSE)
                    .list();
            Map<String, DsBoatDevice> dsBoatDeviceMap = dbsBoatDeviceList.stream()
                    .collect(Collectors.toMap(item -> item.getBoatId() + item.getDeviceType(), Function.identity(), (v1, v2) -> v2));

            Map<String, List<BoatCertificateVO>> boatCertificateMap = new HashMap<>();
            // 查询渔船证书
            if (StringUtils.isNotBlank(getBoatListReq.getOperatorUserId())) {
                // 渔船实际经营人不为空，则为渔船实际经营人角色 查询相关渔船所有证书
                boatCertificateMap = this.findBoatCertificateVOList(boatIdList);
            }

            Map<String, List<BoatCertificateVO>> finalBoatCertificateMap = boatCertificateMap;
            dsBoatData.forEach(item -> {
                if (dsBoatDeviceMap.containsKey(item.getBoatId() + "AIS")) {
                    DsBoatDevice dsBoatDevice = dsBoatDeviceMap.get(item.getBoatId() + "AIS");
                    item.setAisId(dsBoatDevice.getId())
                            .setAisCode(dsBoatDevice.getMmsi())
                            .setTerminalCode(dsBoatDevice.getMmsi());
                }
                if (dsBoatDeviceMap.containsKey(item.getBoatId() + "Beidou")) {
                    DsBoatDevice dsBoatDevice = dsBoatDeviceMap.get(item.getBoatId() + "Beidou");
                    item.setBeidouId(dsBoatDevice.getId())
                            .setBeidouCode(dsBoatDevice.getMmsi());
                }
                if (finalBoatCertificateMap.containsKey(item.getBoatId())) {
                    item.setBoatCertificateList(finalBoatCertificateMap.get(item.getBoatId()));
                }
            });
        }
        return dsBoatData;
    }

    @Override
    public AjaxResult saveBoat(SaveBoatReq saveBoatReq) {
        DsBoatData dsBoatData = new DsBoatData();
        BeanUtils.copyBeanProp(dsBoatData, saveBoatReq);
        //默认添加的是本地渔船
        LambdaQueryWrapper<DsBoatData> query = Wrappers.lambdaQuery();
        query.eq(DsBoatData::getBoatName, saveBoatReq.getBoatName());
        if (count(query) > 0) {  //渔船名称重复
            return AjaxResult.error(ReturnMsgConstants.BOAT_NAME_IS_EXIT.replace("%s", saveBoatReq.getBoatName()));
        }
        String userName = SecurityUtils.getLoginUser().getUser().getNickName();
        dsBoatData.setBuildDate(DateUtils.parseDate(saveBoatReq.getBuildDate()))
                .setCreateBy(userName)
                .setCreateAt(DateUtils.getNowDate())
                .setUpdateBy(userName)
                .setUpdateAt(DateUtils.getNowDate());
        if (save(dsBoatData)) {
            Map<String, String> deviceMap = new LinkedHashMap<String, String>() {{
                put("AIS", saveBoatReq.getAisCode());
                put("Beidou", saveBoatReq.getBeidouCode());
            }};
            List<DsBoatDevice> dsBoatDeviceList = new ArrayList<>();
            deviceMap.forEach((deviceType, deviceCode) -> {
                if (StringUtils.isNotBlank(deviceCode)) {
                    dsBoatDeviceList.add(new DsBoatDevice()
                            .setBoatId(dsBoatData.getBoatId())
                            .setBoatName(dsBoatData.getBoatName())
                            .setDeviceType(deviceType)
                            .setMmsi(deviceCode)
                            .setCreateBy(userName)
                            .setCreateAt(DateUtils.getNowDate())
                            .setUpdateBy(userName)
                            .setUpdateAt(DateUtils.getNowDate())
                    );
                }
            });
            dsBoatDeviceService.saveBatch(dsBoatDeviceList);

            return AjaxResult.success(ReturnMsgConstants.BOAT_ADD_SUCCESS);
        }
        return AjaxResult.error(ReturnMsgConstants.BOAT_ADD_ERROR);
    }

    @Override
    public AjaxResult updateBoat(SaveBoatReq saveBoatReq) {
        String userName = SecurityUtils.getLoginUser().getUser().getNickName();
        DsBoatData dsBoatData = new DsBoatData();
        BeanUtils.copyBeanProp(dsBoatData, saveBoatReq);
        String name = dsBoatData.getBoatName();
        DsBoatData dsBoatData2 = getById(dsBoatData.getBoatId());
        if (!name.equals(dsBoatData2.getBoatName())) {
            LambdaQueryWrapper<DsBoatData> query = Wrappers.lambdaQuery();
            query.eq(DsBoatData::getBoatName, name);
            if (count(query) > 0) {  //渔船名称重复
                return AjaxResult.error(ReturnMsgConstants.BOAT_NAME_IS_EXIT.replace("%s", name));
            }
        }
        dsBoatData.setBuildDate(DateUtils.parseDate(saveBoatReq.getBuildDate()))
                .setUpdateBy(userName)
                .setCreateAt(DateUtils.getNowDate())
                // 将之前的渔船实际经营人信息保存
                .setOperatorName(dsBoatData2.getOperatorName())
                .setOperatorPhone(dsBoatData2.getOperatorPhone())
                .setOperatorIdCard(dsBoatData2.getOperatorIdCard())
                .setOperatorUserId(dsBoatData2.getOperatorUserId())
        ;
        if (updateById(dsBoatData)) {
            // 修改 渔船终端设备
            List<DsBoatDevice> dsBoatDeviceList = new ArrayList<>();
            Map<String, String> deviceMap = new LinkedHashMap<String, String>() {{
                put("AIS", saveBoatReq.getAisCode());
                put("Beidou", saveBoatReq.getBeidouCode());
            }};
            deviceMap.forEach((deviceType, deviceCode) -> {
                if (StringUtils.isNotBlank(deviceCode)) {
                    dsBoatDeviceList.add(new DsBoatDevice()
                            .setBoatId(dsBoatData.getBoatId())
                            .setBoatName(dsBoatData.getBoatName())
                            .setDeviceType(deviceType)
                            .setMmsi(deviceCode)
                            .setUpdateBy(userName)
                            .setUpdateAt(DateUtils.getNowDate()));
                }
            });
            dsBoatDeviceService.updateBatchById(dsBoatDeviceList);
            return AjaxResult.success(ReturnMsgConstants.BOAT_UPDATE_SUCCESS);
        }
        return AjaxResult.error(ReturnMsgConstants.BOAT_UPDATE_ERROR);
    }

    /**
     * 根据渔船主键获取渔船相关证书
     *
     * @param boatIdList:渔船主键列表
     * @return java.util.Map<java.lang.String, java.util.List < com.ltgk.boat.domain.vo.BoatCertificateVO>>
     * @author Qi ChengBin
     * @date 2025/4/8
     */
    private Map<String, List<BoatCertificateVO>> findBoatCertificateVOList(List<String> boatIdList) {

        GetBoatCredentialOverDueListReq req = new GetBoatCredentialOverDueListReq()
                .setBoatIdList(boatIdList);
        List<BoatCredentialOverDueVo> findBoatCredentialOverDueList = baseMapper.getBoatCredentialOverDueList(req);

        return findBoatCredentialOverDueList.stream()
                // 1. 按 boatId 进行分组
                .collect(Collectors.groupingBy(BoatCredentialOverDueVo::getBoatId))
                .entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().stream()
                                // 2. 按 certificateType 二次分组
                                .collect(Collectors.groupingBy(BoatCredentialOverDueVo::getCertificateType,
                                        // 3. 取每组最新记录
                                        Collectors.maxBy(Comparator.comparing(BoatCredentialOverDueVo::getCreateAt))
                                ))
                                .values().stream()
                                // 4. 过滤并转换对象
                                .filter(Optional::isPresent)
                                .map(Optional::get)
                                .map(vo -> new BoatCertificateVO(
                                        vo.getCertificateType(),
                                        vo.getCertificateCode()
                                ))
                                .collect(Collectors.toList())
                ));
    }

    @Override
    public void export(HttpServletResponse response, GetBoatListReq getBoatListReq) throws Throwable {
        getBoatListReq.setPageNum(1);
        getBoatListReq.setPageSize(99999);
        List<DsBoatData> list = this.getBoatList(getBoatListReq);
        List<ExportBoatDataResp> boatDataVos = Lists.newArrayList();
        list.forEach(item -> {
            ExportBoatDataResp exportBoatDataResp = new ExportBoatDataResp();
            BeanUtils.copyBeanProp(exportBoatDataResp, item);
            boatDataVos.add(exportBoatDataResp);
        });
        ExcelUtil<ExportBoatDataResp> util = new ExcelUtil<>(ExportBoatDataResp.class);
        util.exportExcel(response, boatDataVos, "渔船档案");
    }

    @Override
    public void importTemplate(HttpServletResponse response) {
        List<BotaImportTemplateResp> dsBoatDataExcelVos = Lists.newArrayList();
        ExcelUtil<BotaImportTemplateResp> util = new ExcelUtil<>(BotaImportTemplateResp.class);
        util.exportExcel(response, dsBoatDataExcelVos, "船舶档案信息模板");
    }

    @Override
    public AjaxResult importBoat(MultipartFile file) throws IOException {
        String filename = file.getOriginalFilename();
        if (StringUtils.isEmpty(filename)) {
            throw new RuntimeException(ReturnMsgConstants.IMPORT_FILE_IS_NOT_FOUND);
        }
        if ((!StringUtils.endsWithIgnoreCase(filename, ".xls") && !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) {
            throw new RuntimeException(ReturnMsgConstants.IMPORT_FILE_IS_ERROR);
        }

        ExcelUtil<BotaImportTemplateResp> util = new ExcelUtil<BotaImportTemplateResp>(BotaImportTemplateResp.class);
        List<BotaImportTemplateResp> BoatList = util.importExcel(file.getInputStream());
        if (CollectionUtils.isEmpty(BoatList)) {
            return AjaxResult.error(ReturnMsgConstants.IMPORT_FILE_CONTENT_ERROR);
        }
        List<DsBoatData> dataList = new ArrayList<>();
        Set<String> setName = new HashSet<>();
        for (BotaImportTemplateResp m : BoatList) {
            DsBoatData item = new DsBoatData();
            BeanUtils.copyBeanProp(item, m);
            if (StringUtils.isNotBlank(m.getBuildDate())) {
                item.setBuildDate(DateUtils.parseDate(m.getBuildDate()));
            }
            //验证数据是否存在
            LambdaQueryWrapper<DsBoatData> query = Wrappers.lambdaQuery();
            query.eq(DsBoatData::getBoatName, item.getBoatName());
            query.last("limit 1");
            DsBoatData one = this.getOne(query);
            //如果导入文档中存在重复，则保留第一条的
            if (!setName.contains(item.getBoatName())) {
                if (Objects.isNull(one)) {
                    item.setCreateBy(SecurityUtils.getUserId().toString());
                    item.setCreateAt(DateUtils.getNowDate());
                    item.setUpdateBy(SecurityUtils.getUserId().toString());
                    item.setUpdateAt(DateUtils.getNowDate());
                    dataList.add(item);
                } else {
                    item.setUpdateBy(SecurityUtils.getUserId().toString());
                    item.setUpdateAt(DateUtils.getNowDate());
                    item.setBoatId(one.getBoatId());
                    dataList.add(item);
                }
            }
            setName.add(item.getBoatName());
        }
        //处理数据
        boolean update = true;
        boolean insert = true;
        //先保存再更新
        if (CollectionUtils.isNotEmpty(dataList)) {
            insert = this.saveOrUpdateBatch(dataList);
        }
        return AjaxResult.success(update && insert);
    }

    @Override
    public List<DsBoatCredentialVO> getBoatCredentialList(GetBoatCredentialListReq getBoatCredentialListReq) {
        startPage(getBoatCredentialListReq.getPageNum(), getBoatCredentialListReq.getPageSize());
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        List<DsBoatCredentialVO> dataList = new ArrayList<>();
        //街镇管理员
        if (user.getRoles().contains(Constants.ROLE_TOWN)) {
            dataList = dsBoatCredentialMapper.getBoatCredentialList(getBoatCredentialListReq, user, user.getStreets(), null);
        } else if (user.getRoles().contains(Constants.ROLE_VILLAGE)) {
            //村级管理员
            dataList = dsBoatCredentialMapper.getBoatCredentialList(getBoatCredentialListReq, user, null, user.getVillages());
        } else {
            dataList = dsBoatCredentialMapper.getBoatCredentialList(getBoatCredentialListReq, user, null, null);
        }
        return dataList;
    }

    @Override
    public AjaxResult saveBoatCredential(SaveBoatCredentialReq saveBoatCredentialReq) {
        LambdaQueryWrapper<DsBoatData> query = Wrappers.lambdaQuery();
        query.eq(DsBoatData::getBoatName, saveBoatCredentialReq.getBoatName());
        query.eq(DsBoatData::getDelFlag, Constants.FLAG_FALSE);
        DsBoatData dsBoatData = baseMapper.selectOne(query);
        if (dsBoatData == null) {
            return AjaxResult.error(ReturnMsgConstants.BOAT_NAME_IS_NOT_EXIT.replace("%s", saveBoatCredentialReq.getBoatName()));
        }
        return AjaxResult.success();
    }

    @Override
    public List<DsBoatInsurance> getBoatInsuranceList(GetBoatInsuranceReq getBoatInsuranceReq) {
        // 根据登录用户的角色判断是否是查询个人数据权限
        SysUser user = SecurityUtils.getLoginUser().getUser();
        List<SysRole> loginUserRoleList = user.getRoles();

        if (loginUserRoleList.stream().anyMatch(item -> item.getRoleKey().equals(PersonalDataEnums.PERSONAL_DATA.getValue()))) {
            // 查询渔船实际经营人
            getBoatInsuranceReq.setOperatorUserId(String.valueOf(user.getUserId()));
        }

        startPage(getBoatInsuranceReq.getPageNum(), getBoatInsuranceReq.getPageSize());
        return dsBoatInsuranceMapper.getBoatInsuranceList(getBoatInsuranceReq, null, null);
    }

    @Override
    public AjaxResult getBoatMessages(GetBoatMessagesReq getBoatMessagesReq) {
        LambdaQueryWrapper<DsBoatData> boatDataLambdaQueryWrapper = Wrappers.lambdaQuery();
        boatDataLambdaQueryWrapper.eq(DsBoatData::getBoatName, getBoatMessagesReq.getBoatName());
        boatDataLambdaQueryWrapper.eq(DsBoatData::getDelFlag, Constants.FLAG_FALSE);
        Map<String, Object> paramMap = new HashMap<>();
        //1、 查询船舶基本信息
        DsBoatData boatData = getOne(boatDataLambdaQueryWrapper);
        if (boatData == null) {
            return AjaxResult.error(getBoatMessagesReq.getBoatName() + ",信息不存在！");
        }

        // 根据渔船信息获取 ais 数据 和 北斗数据
        List<DsBoatDevice> dbsBoatDeviceList = dsBoatDeviceService.lambdaQuery()
                .eq(DsBoatDevice::getBoatId, boatData.getBoatId())
                .eq(DsBoatDevice::getDelFlag, Constants.FLAG_FALSE)
                .list();
        Map<String, DsBoatDevice> dsBoatDeviceMap = dbsBoatDeviceList.stream()
                .collect(Collectors.toMap(item -> item.getBoatId() + item.getDeviceType(), Function.identity(), (v1, v2) -> v2));
        if (dsBoatDeviceMap.containsKey(boatData.getBoatId() + "AIS")) {
            boatData.setAisId(dsBoatDeviceMap.get(boatData.getBoatId() + "AIS").getId());
            boatData.setAisCode(dsBoatDeviceMap.get(boatData.getBoatId() + "AIS").getMmsi());
        }
        if (dsBoatDeviceMap.containsKey(boatData.getBoatId() + "Beidou")) {
            boatData.setBeidouId(dsBoatDeviceMap.get(boatData.getBoatId() + "Beidou").getId());
            boatData.setBeidouCode(dsBoatDeviceMap.get(boatData.getBoatId() + "Beidou").getMmsi());
        }

        //2、 查询船主信息
        LambdaQueryWrapper<DsBoatName> nameLqw = new LambdaQueryWrapper<>();
        nameLqw.eq(DsBoatName::getBoatId, boatData.getBoatId());
        nameLqw.orderByDesc(DsBoatName::getCreateAt);
        nameLqw.last("limit 1");
        DsBoatName boatName = dsBoatNameService.getOne(nameLqw);
        //4、 查询证书信息 -- 捕捞证书信息
        List<BoatCertificateResp> certificateList = new ArrayList<>();
        LambdaQueryWrapper<DsBoatFishing> fishingLqw = new LambdaQueryWrapper<>();
        fishingLqw.eq(DsBoatFishing::getBoatId, boatData.getBoatId());
        fishingLqw.orderByDesc(DsBoatFishing::getCreateAt);
        fishingLqw.last("limit 1");
        DsBoatFishing boatFishing = dsBoatFishingService.getOne(fishingLqw);
        if (boatFishing != null) {
            BoatCertificateResp boatCertificateResp = new BoatCertificateResp();
            boatCertificateResp.setBoatName(boatData.getBoatName());
            boatCertificateResp.setCertificateType("捕捞许可证");
            boatCertificateResp.setCertificateCode(boatFishing.getFishingCode());
            boatCertificateResp.setIssueDate(boatFishing.getSignDate());
            boatCertificateResp.setIssueDept(boatFishing.getSignDept());
            boatData.setMainWorkType(boatFishing.getMainWorkType());
            certificateList.add(boatCertificateResp);
        }
        // 国籍登记证书信息
        LambdaQueryWrapper<DsBoatNationality> nationalityLqw = new LambdaQueryWrapper<>();
        nationalityLqw.eq(DsBoatNationality::getBoatId, boatData.getBoatId());
        nationalityLqw.orderByDesc(DsBoatNationality::getCreateAt);
        nationalityLqw.last("limit 1");
        DsBoatNationality boatNationality = dsBoatNationalityService.getOne(nationalityLqw);
        if (boatNationality != null) {
            BoatCertificateResp boatCertificateResp = new BoatCertificateResp();
            boatCertificateResp.setBoatName(boatData.getBoatName());
            boatCertificateResp.setCertificateType("国籍登记证书");
            boatCertificateResp.setCertificateCode(boatNationality.getNationalityCode());
            boatCertificateResp.setIssueDate(boatNationality.getApprovalDate());
            boatCertificateResp.setIssueDept(boatNationality.getApprovalAuthority());
            boatCertificateResp.setStartTime(boatNationality.getApplyDate());
            boatCertificateResp.setEndTime(boatNationality.getCertificateValidity());
            certificateList.add(boatCertificateResp);
        }
        paramMap.put("certificateList", certificateList);
        // 查找渔船绑定的船员信息
        DsBoatBindCrewDTO dsBoatBindCrewDTO = new DsBoatBindCrewDTO();
        dsBoatBindCrewDTO.setBoatName(boatData.getBoatName());
        List<DsBoatBindCrew> boatBindCrewByBoatName = dsBoatBindCrewService.findBoatBindCrewByBoatName(dsBoatBindCrewDTO);
        paramMap.put("crew", boatBindCrewByBoatName);
        List<DsEnterOutPort> enterOutPortList = dsEnterOutPortService.getEnterOutPortList(boatData.getBoatName());
        paramMap.put("enterOutPort", enterOutPortList);

        //5、年检信息:没有年检信息则亮黄码
        Date now = new Date();
        LambdaQueryWrapper<DsAnnualInspection> dai = Wrappers.lambdaQuery();
        dai.eq(DsAnnualInspection::getBoatName, getBoatMessagesReq.getBoatName());
        dai.eq(DsAnnualInspection::getDelFlag, 0);
        dai.isNotNull(DsAnnualInspection::getValidUntil);
        dai.orderByDesc(DsAnnualInspection::getValidUntil);
        List<DsAnnualInspection> list = dsAnnualInspectionService.list(dai);
        String annInspectionColor = "GREEN"; //年检安全码颜色
        if (CollectionUtils.isNotEmpty(list)) {
            DsAnnualInspection dsAnnualInspection = list.get(0); //最新的证书号信息
            Date validUntil = dsAnnualInspection.getValidUntil();
            if (now.after(validUntil)) {
                annInspectionColor = "RED";
            } else {
                Calendar rightNow = Calendar.getInstance();
                rightNow.setTime(validUntil); //有效期至
                rightNow.add(Calendar.MONTH, -1); //减去一月
                ////有效期在年检证书过期前一个月内，亮黄码
                if (now.after(rightNow.getTime())) {
                    annInspectionColor = "YELLOW";
                }
            }
            paramMap.put("validUntil", validUntil);
        } else {
            annInspectionColor = "RED";
        }
        paramMap.put("annInspectionColor", "RED".equals(annInspectionColor) ? "RED" : "GREEN"); //年检信息安全码颜色
        paramMap.put("annualInspection", list);

        //7、渔船-违规信息：todo 暂定现在已存在未受理或正在受理违规信息，任务渔船处于违规状态，已处理的违规默认是违规历史
        LambdaQueryWrapper<DsBoatIllegal> queryDbi = Wrappers.lambdaQuery();
        queryDbi.eq(DsBoatIllegal::getBoatName, getBoatMessagesReq.getBoatName());
        queryDbi.eq(DsBoatIllegal::getDelFlag, 0);
        List<DsBoatIllegal> listIllegal = dsBoatIllegalMapper.selectList(queryDbi);
        paramMap.put("boatData", boatData);
        paramMap.put("illegal", listIllegal); //保存渔船违规信息

        // 查询渔船信用相关数据
        FindBoatCreditReq req = new FindBoatCreditReq()
                .setBoatName(boatData.getBoatName());
        List<DsBoatCreditVO> boatCreditList = dsBoatIllegalMapper.findBoatCreditList(req);
        // 渔船信用相关数据
        paramMap.put("boatCreditList", boatCreditList);
        return AjaxResult.success(paramMap);
    }

    /**
     * 根据渔船id获取渔船信息
     *
     * @param boatId
     * @return
     */
    @Override
    public DsBoatData getBoatById(String boatId, String boatName) {
        LambdaQueryWrapper<DsBoatData> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ObjectUtils.isNotEmpty(boatId), DsBoatData::getBoatId, boatId);
        lqw.or();
        lqw.eq(ObjectUtils.isNotEmpty(boatName), DsBoatData::getBoatName, boatName);
        lqw.orderByDesc(DsBoatData::getCreateAt).last("limit 1");
        return this.getOne(lqw);
    }

    /**
     * 分页查询渔船信用列表
     *
     * @param req: 分页查询渔船信用列表请求参数
     * @return com.ltgk.common.core.page.TableDataInfo
     * @author Qi ChengBin
     * @date 2025/2/27
     */
    @Override
    public List<DsBoatCreditVO> findBoatCreditList(FindBoatCreditReq req) {
        startPage(req.getPageNum(), req.getPageSize());
        List<DsBoatCreditVO> boatCreditList = dsBoatIllegalMapper.findBoatCreditList(req);

        return boatCreditList;
    }

    /**
     * 业务模块首页渔船查询数量视图
     *
     * @return com.ltgk.boat.domain.vo.BusinessIndexStatisticsVO
     * @author Qi ChengBin
     * @date 2025/3/4
     */
    @Override
    public BusinessIndexStatisticsVO getBusinessIndexStatistics() {
        BusinessIndexStatisticsVO result = new BusinessIndexStatisticsVO();
        // 获取当天最早时间
        Date beginOfDay = DateUtil.beginOfDay(new Date());
        // 卡口抓拍数据       珏壮要求不展示 船型抓拍数据【未检测到船名】
        result.setCameraCount(baseMapper.getCameraCount(beginOfDay));
        // 渔船总数
        Long boatCount = baseMapper.selectBoatCount(null);
        result.setBoatCount(boatCount);
        // 在线数量
        // 获取2h 之前时间  2h beforeTwoHourDate
        Date beforeTwoHourDate = DateUtil.offset(new Date(), DateField.HOUR_OF_DAY, -2);
        Long onlineCount = baseMapper.getOnlineCount(beforeTwoHourDate);
        result.setOnlineCount(onlineCount);
        // 离线数量
        result.setOfflineCount(boatCount - onlineCount);
        // 在外作业船数
        result.setOutSeaCount((long) portStatisticsService.getOutPortList(new GetOutPortListReq()).size());
        // 预警总数量
        LambdaQueryWrapper<BoatAlarmInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(BoatAlarmInfo::getDelFlag, 0);
        result.setAlarmCount(boatAlarmInfoService.count(lqw));
        return result;
    }

    /**
     * 根据渔船id 获取渔船相关信息
     *
     * @param req:
     * @return com.ltgk.boat.domain.vo.GetBoatInfoByBoatIdVO
     * @author Qi ChengBin
     * @date 2025/4/2
     */
    @Override
    public BoatInfoByBoatIdVO GetBoatInfoByBoatId(GetBoatInfoByBoatIdReq req) {
        BoatInfoByBoatIdVO boatInfoByBoatIdVO = new BoatInfoByBoatIdVO();
        if (StringUtils.isNotBlank(req.getBoatId())) {
            DsBoatData dsBoatData = this.getBoatById(req.getBoatId(), null);
            BeanUtils.copyBeanProp(boatInfoByBoatIdVO, dsBoatData);
            if (ObjectUtils.isNotEmpty(dsBoatData)) {
                List<DsBoatDevice> dbsBoatDeviceList = dsBoatDeviceService
                        .lambdaQuery()
                        .eq(DsBoatDevice::getBoatId, req.getBoatId())
                        .eq(DsBoatDevice::getDelFlag, Constants.FLAG_FALSE)
                        .list();
                Map<String, DsBoatDevice> dsBoatDeviceMap = dbsBoatDeviceList.stream()
                        .collect(Collectors.toMap(item -> item.getBoatId() + item.getDeviceType(), Function.identity(), (v1, v2) -> v2));
                if (dsBoatDeviceMap.containsKey(dsBoatData.getBoatId() + "AIS")) {
                    boatInfoByBoatIdVO.setAisCode(dsBoatDeviceMap.get(dsBoatData.getBoatId() + "AIS").getMmsi());
                }
                if (dsBoatDeviceMap.containsKey(dsBoatData.getBoatId() + "Beidou")) {
                    boatInfoByBoatIdVO.setBeidouCode(dsBoatDeviceMap.get(dsBoatData.getBoatId() + "Beidou").getMmsi());
                }
            }
        } else {
            // 查不出数据来，也给前端传递的 boatId 传递回去，以便错误数据排查
            boatInfoByBoatIdVO.setBoatId(req.getBoatId());

        }
        if (StringUtils.isBlank(boatInfoByBoatIdVO.getAisCode()) && StringUtils.isNotBlank(req.getDeviceType()) && req.getDeviceType().contains("AIS")) {
            // 根据前端传递的 设备类型和终端号设置 aisCode
            boatInfoByBoatIdVO.setAisCode(req.getTerminalCode());
        }
        if (StringUtils.isBlank(boatInfoByBoatIdVO.getBeidouCode()) && StringUtils.isNotBlank(req.getDeviceType()) && req.getDeviceType().contains("Beidou")) {
            // 根据前端传递的 设备类型和终端号设置 beidouCode
            boatInfoByBoatIdVO.setBeidouCode(req.getTerminalCode());
        }
        return boatInfoByBoatIdVO;
    }

    @Override
    public List<PointSolrEntity> findSeaBoatList(FindSeaBoatListReq req) {
        // 查询所有的渔港区域
        List<DsPort> dsPortList = dsPortService.lambdaQuery()
                .eq(DsPort::getDelFlag, Constants.FLAG_FALSE)
                .list();

        // 根据港口信息组装 查询的 x y radius 数据
        List<Map<String, Object>> xyRadiusList = assembledXyRadiusList(dsPortList);

        // 创建查询的根条件
        Criteria criteria = new Criteria("locationTime")
                .between(DateUtils.toDate(req.getStartTime()), DateUtils.toDate(req.getEndTime()));

        Criteria nearCriteria = null;
        // 遍历 xyRadiusList，构造每个点位的 near 查询条件，并添加到 nearCriteriaList
        for (Map<String, Object> xyRadius : xyRadiusList) {
            Double x = (Double) xyRadius.get("x");
            Double y = (Double) xyRadius.get("y");
            Double radius = (Double) xyRadius.get("radius");
            // 创建每个点位的 near 查询条件
            Point center = new Point(y, x);
            Distance distance = new Distance(radius / 1000, Metrics.KILOMETERS); // 将半径转换为公里
            // 创建每个 point 的 near 查询条件
            if (Objects.isNull(nearCriteria)) {
                nearCriteria = new Criteria("storeLocation").near(center, distance);
            } else {
                nearCriteria = nearCriteria.or("storeLocation").near(center, distance);
            }
        }

        // 如果有船名过滤条件，则添加船名查询
        if (StringUtils.isNotBlank(req.getBoatName())) {
            criteria = criteria.and("boatName").contains(req.getBoatName());
        }

        // 如果又船籍过滤条件，则添加船籍查询
        if (StringUtils.isNotBlank(req.getHomePort())) {
            criteria = criteria.and("homePort").contains(req.getHomePort());
        }
        criteria = criteria.and(nearCriteria);

        // 创建 SimpleQuery 并设置排序和分组
        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);
        return page.getContent();
    }

    @Override
    public AjaxResult deleteBoat(DeleteByIdsReq deleteByIdsReq) {
        List<String> idList = Arrays.asList(deleteByIdsReq.getIds().split(","));
        if (CollUtil.isEmpty(idList)) {
            return AjaxResult.error(ReturnMsgConstants.DELETE_ID_NOT_EMPTY);
        }
        if (this.removeByIds(idList)) {
            return AjaxResult.success(ReturnMsgConstants.BOAT_DELETE_SUCCESS);
        }
        return AjaxResult.error(ReturnMsgConstants.BOAT_DELETE_ERROR);
    }

    /**
     * 根据渔港信息组装查询的范围 x y radius 的数据
     *
     * @param dsPortList:
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     * @author Qi ChengBin
     * @date 2025/5/19
     */
    private List<Map<String, Object>> assembledXyRadiusList(List<DsPort> dsPortList) {

        List<Map<String, Object>> resList = new ArrayList<>();
        for (DsPort dsPort : dsPortList) {
            List<Point> points = new ArrayList<>();
            // 渔港全是不规则图形
            String[] split = dsPort.getPositionInfo().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);
            Map<String, Object> resMap = new HashMap<>();
            resMap.put("y", centerPoint.getCenterPoint().getLatitude().doubleValue());
            resMap.put("x", centerPoint.getCenterPoint().getLongitude().doubleValue());
            resMap.put("radius", centerPoint.getDistance().doubleValue() * 1000);
            resList.add(resMap);
        }
        return resList;
    }
}
