package com.ltgk.video.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import com.hikvision.artemis.sdk.ArtemisHttpUtil;
import com.ltgk.common.config.SmartFisheryPortConfig;
import com.ltgk.common.constant.Constants;
import com.ltgk.common.core.domain.AjaxResult;
import com.ltgk.common.utils.CameraDegreesUtil;
import com.ltgk.common.utils.DateUtils;
import com.ltgk.common.utils.StringUtils;
import com.ltgk.common.utils.http.HttpUtils;
import com.ltgk.common.utils.sql.MybatisUtil;
import com.ltgk.system.service.ISysLogininforService;
import com.ltgk.video.domain.dto.LinkageParamDto;
import com.ltgk.video.domain.entity.DsVideo;
import com.ltgk.video.domain.entity.DsVideoLog;
import com.ltgk.video.domain.vo.HkDsVideoVO;
import com.ltgk.video.mapper.DsVideoMapper;
import com.ltgk.video.service.DsVideoLogService;
import com.ltgk.video.service.IDsVideoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 视频监控管理表 服务实现类
 * </p>
 *
 * @author zhouyaoyao
 * @since 2025-01-19
 */
@Service
@Slf4j
public class DsVideoServiceImpl extends ServiceImpl<DsVideoMapper, DsVideo> implements IDsVideoService {

    @Resource
    private DsCameraService dsCameraService;

    @Resource
    RedisTemplate redisTemplate;

    @Resource
    IDsVideoService dsVideoService;
    @Resource
    private DsVideoLogService dsVideoLogService;

    private static final String HTTPS_TYPE = "https://";

    /**
     * 能力开放平台的网站路径
     * 路径不用修改，就是/artemis
     */
    private static final String ARTEMIS_PATH = "/artemis";

    /**
     * 手动抓图API
     */
    private static final String MANUAL_CAPTURE_URL = "/api/video/v1/manualCapture";

    /**
     * post请求application/json类型参数
     */
    private static final String CONTENT_TYPE = "application/json";

    /**
     * 图片下载API
     */
    private static final String GET_IMG_URL = "/api/video/v1/events/picture";

    /**
     * 获取监控点在线状态
     */
    public static final String GET_POINT_ONLINE_STATUS_URL = "/api/nms/v1/online/camera/get";
    @Autowired
    private ISysLogininforService iSysLogininforService;

    /**
     * 获取监控设备 PTZ 配置链接
     */
    @Value("${sdk.hk.getPtzCfgUrl}")
    private String hkGetPtzCfgUrl;

    /**
     * 分页查询
     *
     * @param dsVideo
     * @return
     */
    @Override
    public IPage<DsVideo> selectByPage(DsVideo dsVideo) {
        int pageNum = Objects.isNull(dsVideo.getPageNum()) ? 1 : dsVideo.getPageNum();
        int pageSize = Objects.isNull(dsVideo.getPageSize()) ? 10 : dsVideo.getPageSize();
        IPage<DsVideo> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<DsVideo> queryWrapper = MybatisUtil.notNullField(dsVideo).lambda();
        queryWrapper.orderByDesc(DsVideo::getUpdateAt);
        return baseMapper.selectPage(page, queryWrapper);
    }

    //    @PostConstruct
    @Scheduled(cron = "0 0 */2 * * *")
    public void startAlarmChanTask() {
        LambdaQueryWrapper<DsVideo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(DsVideo::getVideoName, "热成像");
        List<DsVideo> list = list(queryWrapper);
        list.stream().forEach(video -> {
            try {
                dsCameraService.setupAlarmChan(video.getVideoIp(), video.getVideoPort(), video.getVideoAccount(), video.getVideoPassword());
                Thread.sleep(3000);
            } catch (Exception e) {
            }
        });
    }

    /**
     * 根据视频监控设备名称批量添加更新
     *
     * @param dsVideos
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveBatchByName(List<DsVideo> dsVideos) {

        for (DsVideo dsVideo : dsVideos) {
            LambdaUpdateWrapper<DsVideo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(DsVideo::getVideoName, dsVideo.getVideoName());
            if (!update(dsVideo, updateWrapper)) {
                save(dsVideo);
            }
        }

        return true;
    }

    /**
     * 光电随动控制
     *
     * @param msg
     * @return
     */
    public boolean servoByRadar(String msg) {
        JSONObject jsonObject = JSONObject.parseObject(msg);
        if (Objects.isNull(jsonObject)) {
            return false;
        }
        Double longitude = jsonObject.getDouble("longitude");
        Double latitude = jsonObject.getDouble("latitude");
        String targetSourceId = jsonObject.getString("targetSourceId");
        String mmsi = jsonObject.getString("mmsi");
        Integer source = jsonObject.getInteger("source");
        if (Objects.isNull(longitude) || Objects.isNull(latitude) || Objects.isNull(source)) {
            return false;
        }
        if (StringUtils.isNotEmpty(targetSourceId) && redisTemplate.hasKey("servo:" + targetSourceId)) {
            String deviceCode = redisTemplate.opsForValue().get("servo:" + targetSourceId).toString();
            AjaxResult result = sitMoveByGps(deviceCode, longitude.toString(), latitude.toString());
            return result.isSuccess();
        } else if (StringUtils.isNotEmpty(mmsi) && redisTemplate.hasKey("servo:" + mmsi)) {
            String deviceCode = redisTemplate.opsForValue().get("servo:" + mmsi).toString();
            AjaxResult result = sitMoveByGps(deviceCode, longitude.toString(), latitude.toString());
            return result.isSuccess();
        } else {
            return false;
        }
    }

    /**
     * 光电联动反向定位联动控制，根据监控设备和将要联动的定位目标经纬度控制视频监控设备转动对焦
     *
     * @return
     */
    public AjaxResult sitMoveByGps(String deviceCode, String gpsX, String gpsY) {
        LambdaQueryWrapper<DsVideo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DsVideo::getVideoCode, deviceCode);
        queryWrapper.isNotNull(DsVideo::getHeight);
        queryWrapper.last("limit 1");
        DsVideo video = dsVideoService.getOne(queryWrapper);
        Double height = video.getHeight().doubleValue();
        if (StringUtils.isBlank(deviceCode) || Objects.isNull(video)) {
            return AjaxResult.error("暂时没有该编号的监控摄像头，请核实摄像头编号是否正确！");
        }
        String latitude = video.getLatitude().toString();
        String longitude = video.getLongitude().toString();
        int rotateT = video.getRotateT();
        if (StringUtils.isBlank(latitude) || StringUtils.isBlank(longitude)) {
            return AjaxResult.error("该监控摄像头没有经纬度定位信息！");
        }
        try {
            double latitudeVal = Double.parseDouble(latitude);
            double longitudeVal = Double.parseDouble(longitude);
            double gpsXVal = Double.parseDouble(gpsX);
            double gpsYVal = Double.parseDouble(gpsY);
            BigDecimal degreeX = CameraDegreesUtil.rotateForNorthByLR(longitudeVal, latitudeVal, gpsXVal, gpsYVal, rotateT);
            Integer degreeXVal = (degreeX.multiply(new BigDecimal(10))).intValue();
            degreeXVal = degreeXVal < 0 ? 3600 + degreeXVal : degreeXVal;
            BigDecimal degreeY = CameraDegreesUtil.rotateByUD(Objects.isNull(height) ? 0.2 : (height / 1000), longitudeVal, latitudeVal, gpsXVal, gpsYVal);
            Integer degreeYVal = (degreeY.multiply(new BigDecimal(10))).intValue();
            BigDecimal distance = CameraDegreesUtil.getDistance(longitudeVal, latitudeVal, gpsXVal, gpsYVal).setScale(5, RoundingMode.HALF_UP);


            double zNumVal = 1d;
            double yVal = 0d;
            double xVal = 0d;
            String xValArr = video.getXValArr();
            String yValArr = video.getYValArr();
            String zNumValArr = video.getZNumValArr();
            if (StringUtils.isNotBlank(xValArr)) {
                List<LinkageParamDto> xValArray = JSONObject.parseArray(xValArr, LinkageParamDto.class);
                xValArray = xValArray.stream().filter(f -> !Objects.isNull(f.getNumber()) && f.getDistance() >= 0)
                        .sorted(Comparator.comparing(LinkageParamDto::getDistance).reversed())
                        .collect(Collectors.toList());
                // 根据与联动目标的距离调节水平旋转参数
//                if (xValArray.size() == 1) {
//                    xVal = xValArray.get(0).getNumber();
//                } else
//                    if (xValArray.size() > 1) {
//                    for (int i=0;i<xValArray.size();i++) {
//                        LinkageParamDto linkageParamDto = xValArray.get(i);
////                        if (i==xValArray.size()-1) {
////                            xVal = linkageParamDto.getNumber();
////                        } else
//                            if (distance.doubleValue()>=linkageParamDto.getDistance()) {
//                            xVal = linkageParamDto.getNumber();
//                        }
//                    }
//                }
                // 根据与联动目标的相对方位角调节水平旋转参数
                if (!CollectionUtils.isEmpty(xValArray)) {
                    int degreeXValTmp = degreeXVal;
                    Optional<LinkageParamDto> optional = xValArray.stream()
                            .map(m -> {
                                m.setDistance(new BigDecimal((degreeXValTmp / 10) - m.getDistance()).abs()
                                        .setScale(6, RoundingMode.HALF_UP).doubleValue());
                                return m;
                            }).sorted(Comparator.comparing(LinkageParamDto::getDistance)).findFirst();
                    if (optional.isPresent()) {
                        xVal = optional.get().getNumber();
                    }
                }
            }
            // 根据与联动目标的相对距离调节俯仰角参数
            if (StringUtils.isNotBlank(yValArr)) {
                List<LinkageParamDto> yValArray = JSONObject.parseArray(yValArr, LinkageParamDto.class);
                yValArray = yValArray.stream().filter(f -> !Objects.isNull(f.getNumber()) && f.getDistance() >= 0d)
                        .sorted(Comparator.comparing(LinkageParamDto::getDistance).reversed())
                        .collect(Collectors.toList());
//                if (yValArray.size() == 1) {
//                    yVal = yValArray.get(0).getNumber();
//                } else
//                if (yValArray.size() > 1) {
//                    for (int i=0;i<yValArray.size();i++) {
//                        LinkageParamDto linkageParamDto = yValArray.get(i);
//                        if (i==yValArray.size()-1) {
//                            yVal = linkageParamDto.getNumber();
//                        } else
//                        if (distance.doubleValue()>=linkageParamDto.getDistance()) {
//                            yVal = linkageParamDto.getNumber();
//                        }
//                    }
//                }
                if (!CollectionUtils.isEmpty(yValArray)) {
                    BigDecimal distanceTmp = distance;
                    Optional<LinkageParamDto> optional = yValArray.stream()
                            .map(m -> {
                                m.setDistance(new BigDecimal(distanceTmp.doubleValue() - m.getDistance().doubleValue())
                                        .abs().setScale(6, RoundingMode.HALF_UP).doubleValue());
                                return m;
                            })
                            .sorted(Comparator.comparing(LinkageParamDto::getDistance))
                            .findFirst();
                    if (optional.isPresent()) {
                        yVal = optional.get().getNumber();
                    }
                }
            }

            if (StringUtils.isNotBlank(zNumValArr)) {
                List<LinkageParamDto> zValArray = JSONObject.parseArray(zNumValArr, LinkageParamDto.class);
                zValArray = zValArray.stream().filter(f -> !Objects.isNull(f.getNumber()) && f.getDistance() >= 0d)
                        .sorted(Comparator.comparing(LinkageParamDto::getDistance).reversed())
                        .collect(Collectors.toList());
//                if (zValArray.size() == 1) {
//                    Double number = zValArray.get(0).getNumber();
//                    number = Objects.isNull(number) || number==0d ? 1:number.doubleValue();
//                    zNumVal = new BigDecimal(number).doubleValue();
//                } else
//                if (zValArray.size() > 1) {
//                    for (int i=0;i<zValArray.size();i++) {
//                        LinkageParamDto linkageParamDto = zValArray.get(i);
//                        Double number = linkageParamDto.getNumber();
//                        number = Objects.isNull(number) || number==0d ? 1:number.doubleValue();
//                        if (i==zValArray.size()-1) {
//                            zNumVal = new BigDecimal(number).doubleValue();
//                        } else
//                        if (distance.doubleValue()>=linkageParamDto.getDistance()) {
//                            zNumVal = new BigDecimal(number).doubleValue();
//                        }
//                    }
//                }
                if (!CollectionUtils.isEmpty(zValArray)) {
                    BigDecimal distanceTmp = distance;
                    Optional<LinkageParamDto> optional = zValArray.stream()
                            .map(m -> {
                                m.setDistance(new BigDecimal(distanceTmp.doubleValue() - m.getDistance().doubleValue())
                                        .abs().setScale(6, RoundingMode.HALF_UP).doubleValue());
                                return m;
                            })
                            .sorted(Comparator.comparing(LinkageParamDto::getDistance))
                            .findFirst();
                    if (optional.isPresent()) {
                        zNumVal = optional.get().getNumber();
                    }
                }
            }
            degreeYVal = degreeYVal + new BigDecimal(yVal * 10).intValue();
            degreeXVal = degreeXVal + new BigDecimal(xVal * 10).intValue();
            double zVal = (distance.multiply(new BigDecimal(zNumVal))).doubleValue();

            log.debug("degreeXVal:" + degreeXVal);
            log.debug("degreeYVal:" + degreeYVal);
            log.debug("distance:" + distance);
            log.debug("zVal:" + zVal);
            double zMaxVal = 60;
            if (StringUtils.isNotBlank(video.getVideoIp()) && video.getVideoName().contains("热成像")) {
                zMaxVal = Objects.isNull(video.getZRcxVal()) ? 10 : video.getZRcxVal();
            } else {
                zMaxVal = Objects.isNull(video.getZMaxVal()) ? 60 : video.getZMaxVal();
            }
            zVal = zVal > zMaxVal ? zMaxVal : zVal;
            if (StringUtils.isNotEmpty(video.getVideoIp()) && StringUtils.isNotEmpty(video.getVideoPort())
                    && StringUtils.isNotEmpty(video.getVideoAccount()) && StringUtils.isNotEmpty(video.getVideoPassword())) {

                boolean ptzResult = dsCameraService.hkSetPtzCfg(video.getVideoIp(), video.getVideoPort()
                        , video.getVideoAccount(), video.getVideoPassword()
                        , degreeXVal, degreeYVal, new BigDecimal(zVal * 10).setScale(0, RoundingMode.HALF_UP).intValue());
                return ptzResult ? AjaxResult.success() : AjaxResult.error();
            }
        } catch (Exception e) {
            return AjaxResult.error("服务异常：" + e.getMessage());
        } finally {
//            new Thread(() -> {updateVisualResion();}).start();
        }
        return AjaxResult.error();
    }

    /**
     * 调用光电联动摄像头进行拍照，然后将照片保存到本地
     *
     * @param cameraIndexCode:
     * @return com.ltgk.common.core.domain.AjaxResult
     * @author Qi ChengBin
     * @date 2025/6/5
     */
    @Override
    public AjaxResult catchImage(String cameraIndexCode) {
        final String catchImageApi = ARTEMIS_PATH + MANUAL_CAPTURE_URL;
        // 根据现场环境部署确认是http还是https
        Map<String, String> path = Maps.newHashMap();
        path.put(HTTPS_TYPE, catchImageApi);
        JSONObject jsonBody = new JSONObject();

        jsonBody.put("cameraIndexCode", cameraIndexCode);
        String body = jsonBody.toJSONString();
        String response = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, CONTENT_TYPE, null);
        JSONObject result = JSON.parseObject(response);
        if (Constants.SUCCESS_CODE.equals(result.getString(Constants.CODE))) {
            String picUrl = result.getJSONObject("data").getString("picUrl");

            // 上传文件路径
            String filePath = SmartFisheryPortConfig.getUploadPath();
            // 获取当前时间  年月日时分秒毫秒.jpg
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
            String fileName = "/threeNoBoat/" + LocalDateTime.now().format(formatter) + ".jpg";

            try {
                HttpUtil.downloadFile(picUrl, new File(filePath, fileName));
                return AjaxResult.success(fileName);
            } catch (Exception e) {
                log.error("图片下载失败，{}", e);
                return AjaxResult.error("图片下载失败！");
            }
        }
        return AjaxResult.error("抓拍失败！");
    }

    /**
     * 根据海康安防平台获取摄像头在线状态
     *
     * @author Qi ChengBin
     * @date 2025/7/21
     */
    @Override
    public void updateVideoStatusByPointOnlineStatus() {

        List<DsVideo> dsVideoList = dsVideoService.list();
        // 获取所有的监控摄像头code
        List<String> codeList = dsVideoList.stream()
                .map(DsVideo::getVideoCode)
                .collect(Collectors.toList());

        final String getPointOnlineStatusApi = ARTEMIS_PATH + GET_POINT_ONLINE_STATUS_URL;
        // 根据现场环境部署确认是http还是https
        Map<String, String> path = Maps.newHashMap();
        path.put(HTTPS_TYPE, getPointOnlineStatusApi);

        JSONObject jsonBody = new JSONObject();

        jsonBody.put("pageNo", 1);
        jsonBody.put("pageSize", 500);
        jsonBody.put("indexCodes", codeList);
        String body = jsonBody.toJSONString();
        // post请求application/json类型参数
        String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, CONTENT_TYPE, null);

        cn.hutool.json.JSONObject json = JSONUtil.parseObj(result);
        cn.hutool.json.JSONObject data = json.getJSONObject("data");

        // 提取list数组并转换为List<DeviceStatus>
        List<HkDsVideoVO> hkDsVideoVOList = JSONUtil.toList(
                data.getJSONArray("list"),
                HkDsVideoVO.class
        );

        Map<String, String> hkDsVideoVOMap = hkDsVideoVOList.stream()
                .collect(Collectors.toMap(HkDsVideoVO::getIndexCode,
                        item -> item.getOnline().equals(1) ? "正常" : "故障"));
        List<DsVideoLog> logList = new ArrayList<>();
        dsVideoList.forEach(item ->{
                item.setRunStatus(hkDsVideoVOMap.getOrDefault(item.getVideoCode(), "正常"));
                    DsVideoLog dsVideoLog = new DsVideoLog();
                    dsVideoLog.setVideoId(item.getId());
                    dsVideoLog.setVideoCode(item.getVideoCode());
                    dsVideoLog.setVideoName(item.getVideoName());
                    dsVideoLog.setRunStatus(hkDsVideoVOMap.getOrDefault(item.getVideoCode(), "正常"));
                    dsVideoLog.setCreateBy(item.getCreateBy());
                    dsVideoLog.setCreateAt(DateUtils.getNowDate());
                    logList.add(dsVideoLog);
                }
        );
        dsVideoService.updateBatchById(dsVideoList);
        // 录入巡检记录表中
        dsVideoLogService.saveBatch(logList);
    }

    @Override
    public DsVideo getVideoInfo(DsVideo dsVideo) {
        DsVideo dbDsVideo = dsVideoService.getById(dsVideo.getId());

        return getDsVideoPTZ(dbDsVideo);
    }

    /**
     * 更新摄像头可视范围字段状态，并获取所有的开启了可视摄像头范围的相关数据
     *
     * @param dsVideo：需开启摄像头可视范围的相关摄像头数据
     * @return com.ltgk.common.core.domain.AjaxResult
     * @author Qi ChengBin
     * @date 2025/9/15
     */
    @Override
    public List<DsVideo> updateOneAndFindVideoInfoList(DsVideo dsVideo) {
        List<DsVideo> resultDsVideoList = new ArrayList<>();
        if (dsVideoService.updateById(dsVideo)) {
            // 获取所有的开启了可视摄像头范围相关的数据
            List<DsVideo> dsVideoList = dsVideoService.list(new LambdaQueryWrapper<DsVideo>()
                    .eq(DsVideo::getWhetherToTurnOnTheView, 1)
            );
            for (DsVideo video : dsVideoList) {
                resultDsVideoList.add(getDsVideoPTZ(video));
            }
        }
        return resultDsVideoList;
    }

    /**
     * 通过 监控实体获取相关的监控PTZ数据
     *
     * @param dbDsVideo：监控视频实体
     * @return com.ltgk.video.domain.entity.DsVideo
     * @author Qi ChengBin
     * @date 2025/9/15
     */
    private DsVideo getDsVideoPTZ(DsVideo dbDsVideo) {
        String param = "videoIp=" + dbDsVideo.getVideoIp() + "&videoPort=" + dbDsVideo.getVideoPort() + "&videoAccount=" + dbDsVideo.getVideoAccount() + "&videoPass=" + dbDsVideo.getVideoPassword();
        String resultStr = HttpUtils.sendPost(hkGetPtzCfgUrl, param);

        if (StringUtils.isNotBlank(resultStr)) {
            JSONObject resultObj = JSONObject.parseObject(resultStr);
            boolean success = resultObj.getBooleanValue("success");
            if (success) {
                dbDsVideo.setPTZCfg(resultObj.getJSONObject("result"));
            }
        }
        return dbDsVideo;
    }
}
