package com.ningdatech.carapi.open.manage;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.carapi.car.position.model.po.ReqRealTimeMonitorPO;
import com.ningdatech.carapi.car.vehicle.model.po.VehicleCompanyPO;
import com.ningdatech.carapi.common.constant.UserRoleTypeEnum;
import com.ningdatech.carapi.common.util.CodeUtil;
import com.ningdatech.carapi.sys.entity.dto.OperatorQueryDTO;
import com.ningdatech.carapi.user.security.auth.model.UserInfoDetails;
import com.ningdatech.carapi.user.util.LoginUserUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.exception.code.ExceptionCode;
import com.ningdatech.basic.function.VUtils;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.carapi.car.position.contants.PositionContant;
import com.ningdatech.carapi.car.position.entity.VehiclePositionInfo;
import com.ningdatech.carapi.car.position.model.po.ReqTrajectoryDataPO;
import com.ningdatech.carapi.car.position.model.vo.ResRealTimeMonitorVehicleGisListVO;
import com.ningdatech.carapi.car.position.model.vo.ResTrajectoryDataVO;
import com.ningdatech.carapi.car.position.service.IVehiclePositionInfoService;
import com.ningdatech.carapi.car.vehicle.model.NdVehicleBaseInfo;
import com.ningdatech.carapi.common.constant.VehicleLimitSpeedEnum;
import com.ningdatech.carapi.common.constant.VehicleTypeEnum;
import com.ningdatech.carapi.common.util.SignUtil;
import com.ningdatech.carapi.open.model.po.ReqRealTimeCapPositionPO;
import com.ningdatech.carapi.sys.entity.dto.CompanyDTO;
import com.ningdatech.carapi.sys.helper.CompaniesCacheHelper;
import com.ningdatech.carapi.sys.helper.VehiclesCacheHelper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 车辆位置信息
 *
 * @author CMM
 * @since 2023/11/17 09:35
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OpenCarPositionManage {

    private final CompaniesCacheHelper companiesCacheHelper;
    private final VehiclesCacheHelper vehiclesCacheHelper;
    private final IVehiclePositionInfoService iVehiclePositionInfoService;
    private final RedisTemplate redisTemplate;
    private final RestTemplate restTemplate;
    @Value("${car.interfaceKey}")
    private String carPositionKey;
    @Value("${gps.host}")
    private String HOST;

    public List<ResTrajectoryDataVO> trajectoryDatalist(ReqTrajectoryDataPO po) {

        // 校验签名
        Map<String, Object> paramMap = new HashMap<>(16);
        paramMap.put("carPlate", po.getCarPlate());
        // 根据传入的秘钥生成签名
        String appKey = po.getKey();
        String sign = SignUtil.generateSign(paramMap, appKey);
        String interfaceKey = SignUtil.generateSign(paramMap, carPositionKey);
        if (!sign.equals(interfaceKey)){
            log.error("无权访问轨迹回放接口!");
            throw new BizException("签名错误!");
        }
        String carPlate = po.getCarPlate();
        LocalDateTime startTime = po.getStartTime();
        LocalDateTime endTime = po.getEndTime();
        if (Objects.isNull(startTime)) {
            startTime = LocalDateTime.now().minusHours(2);
        }
        // 默认时间区间为2小时前到现在
        if (Objects.isNull(endTime)) {
            endTime = LocalDateTime.now();
        }

        // 获取车辆基本信息
        NdVehicleBaseInfo car = vehiclesCacheHelper.getByCarPlate(carPlate);
        VUtils.isTrue(Objects.isNull(car)).throwMessage("该车辆不在系统中!");

        //判断日期有没有跨天
        checkDateExtendIntoNextDay(startTime, endTime);

        //动态表名
        List<VehiclePositionInfo> vehiclePositionInfoList = iVehiclePositionInfoService
                .dynamicGpsTableHistorySearch(carPlate, startTime, endTime, NdDateUtils.format(startTime, "yyyy_MM_dd"));

        List<ResTrajectoryDataVO> resTrajectoryDataVOList = vehiclePositionInfoList.stream().map(
                r -> {
                    ResTrajectoryDataVO vo = BeanUtil.copyProperties(r, ResTrajectoryDataVO.class);
                    vo.setIsOverSpeed(checkIsOverSpeed(r,car));
                    return vo;
                }).collect(Collectors.toList());
        //连续6次超速 才算超速
        checkContinuousOverspeed(resTrajectoryDataVOList);
        return resTrajectoryDataVOList;
    }

    public PageVo<ResRealTimeMonitorVehicleGisListVO> realTimeMonitorAllGisList(ReqRealTimeCapPositionPO po) {
        // 根据传入的秘钥生成签名
        String appKey = po.getKey();
        String sign = appKey;
        Integer pageNumber = po.getPageNumber();
        Integer pageSize = po.getPageSize();

        // 校验签名
        String interfaceKey = carPositionKey;
        if (!sign.equals(interfaceKey)){
            log.error("无权访问车辆实时位置接口!");
            throw new BizException("签名错误!");
        }
        int total = vehiclesCacheHelper.all().size();
        List<VehicleCompanyPO> carPlates = vehiclesCacheHelper.all().stream()
                .skip((pageNumber - 1) * pageSize) //跳过前面的元素
                .limit(pageSize)
                .map(vehicles -> BeanUtil.copyProperties(vehicles,VehicleCompanyPO.class))
                .collect(Collectors.toList());
        log.info("request real carPlates 【{}】:", JSON.toJSONString(carPlates));
        if (CollUtil.isEmpty(carPlates)) {
            return PageVo.empty();
        }
        Map<String, Object> map = Maps.newConcurrentMap();
        map.put(PositionContant.FIELD_CAR_PLATE, carPlates.stream().map(VehicleCompanyPO::getCarPlate).collect(Collectors.joining(",")));
        map.put(PositionContant.HOST_FEILD, HOST);
        JSONObject response = restTemplate.getForObject(PositionContant.REAL_GPS_URL, JSONObject.class, map);
        log.info("request real gps 【{}】:", JSON.toJSONString(response));
        if (Objects.isNull(response)) {
            throw new BizException(ExceptionCode.BAD_REQUEST.getCode(), "实时数据请求失败 网络异常");
        }
        Integer statusCode = response.getInteger("status_code");
        if (0 != statusCode) {
            throw new BizException(ExceptionCode.BAD_REQUEST.getCode(), response.getString("message"));
        }
        JSONArray resultJsonArray = response.getJSONArray("result");
        if (Objects.isNull(resultJsonArray) || resultJsonArray.size() == 0) {
            return PageVo.empty();
        }
        Map<String, VehicleCompanyPO> carMap = carPlates.stream()
                .filter(w -> Objects.nonNull(w) && Objects.nonNull(w.getCarPlate()))
                .filter(CodeUtil.distinctByKey(VehicleCompanyPO::getCarPlate)).collect(Collectors.toMap(VehicleCompanyPO::getCarPlate, v -> v));
        List<ResRealTimeMonitorVehicleGisListVO> res =  resultJsonArray.stream().map(o -> {
            JSONObject j = JSON.parseObject(JSON.toJSONString(o));
            String carPlate = j.getString(PositionContant.FIELD_CAR_PLATE);
            VehicleCompanyPO vehicleCompany = carMap.get(carPlate);
            ResRealTimeMonitorVehicleGisListVO vo = new ResRealTimeMonitorVehicleGisListVO();
            vo.setCarLongitude(new BigDecimal(String.valueOf(j.getOrDefault(PositionContant.LONGITUDE,BigDecimal.valueOf(0L)))));
            vo.setCarLatitude(new BigDecimal(String.valueOf(j.getOrDefault(PositionContant.LATITUDE,BigDecimal.valueOf(0L)))));
            vo.setCarAltitude(new BigDecimal(String.valueOf(j.getOrDefault(PositionContant.ALTITUDE,BigDecimal.valueOf(0L)))));
            vo.setCarDirection(new BigDecimal(String.valueOf(j.getOrDefault(PositionContant.DIRECTION,BigDecimal.valueOf(0L)))));
            vo.setCarPlate(carPlate);
            vo.setPlateColor(j.getString(PositionContant.PLATE_COLOR));
            vo.setCompanyId(vehicleCompany.getCompanyId());
            vo.setCompanyName(vehicleCompany.getCompanyName());
            vo.setCarVelocity(new BigDecimal(String.valueOf(j.getOrDefault(PositionContant.CAR_VELOCITY,BigDecimal.valueOf(0L)))));
            vo.setUpdateTime((j.getString(PositionContant.UPDATE_TIME)));
            vo.setSelfNo(vehicleCompany.getSelfNo());
            vo.setStatus(j.getInteger(PositionContant.ONLINE_STATUS));
            vo.setMoving(vehicleCompany.getMoving());
            vo.setVehicleType(vehicleCompany.getVehicleType());
            vo.setVehicleTypeName(vehicleCompany.getVechileTypeName());
            vo.setLimitSpeed(VehicleLimitSpeedEnum.getLimitSpeedByCode(vehicleCompany.getVehicleType()));
            return vo;
        }).collect(Collectors.toList());
        return PageVo.of(res,total);
    }

    public List<ResRealTimeMonitorVehicleGisListVO> realTimeMonitorVehicleGisData(ReqRealTimeCapPositionPO po) {

        Map<String, Object> paramMap = new HashMap<>(16);
        paramMap.put("carPlate", po.getCarPlate());
        // 根据传入的秘钥生成签名
        String appKey = po.getKey();
        String sign = SignUtil.generateSign(paramMap, appKey);
        // 校验签名
        String interfaceKey = SignUtil.generateSign(paramMap, carPositionKey);
        if (!sign.equals(interfaceKey)){
            log.error("无权访问车辆实时位置接口!");
            throw new BizException("签名错误!");
        }

        String carNo = po.getCarPlate();
        // 获取车辆基本信息
        NdVehicleBaseInfo car = vehiclesCacheHelper.getByCarPlate(carNo);
        VUtils.isTrue(Objects.isNull(car)).throwMessage("该车辆不在系统中!");
        
        Map<String, Object> map = Maps.newConcurrentMap();
        map.put(PositionContant.FIELD_CAR_PLATE, carNo);
        map.put(PositionContant.HOST_FEILD, HOST);
        JSONObject response = restTemplate.getForObject(PositionContant.REAL_GPS_URL, JSONObject.class, map);
        if (Objects.isNull(response)) {
            throw new BizException(ExceptionCode.BAD_REQUEST.getCode(), "实时数据请求失败 网络异常");
        }
        Integer statusCode = response.getInteger("status_code");
        if (0 != statusCode) {
            throw new BizException(ExceptionCode.BAD_REQUEST.getCode(), response.getString("message"));
        }
        JSONArray resultJsonArray = response.getJSONArray("result");
        if (Objects.isNull(resultJsonArray) || resultJsonArray.size() == 0) {
            return Collections.emptyList();
        }
        return resultJsonArray.stream().map(o -> {
            LinkedHashMap j = (LinkedHashMap) o;
            String carPlate = String.valueOf(j.get(PositionContant.FIELD_CAR_PLATE));
            ResRealTimeMonitorVehicleGisListVO vo = new ResRealTimeMonitorVehicleGisListVO();
            vo.setCarLongitude(BigDecimal.valueOf(Double.parseDouble(String.valueOf(j.getOrDefault(PositionContant.LONGITUDE, BigDecimal.valueOf(0L))))));
            vo.setCarLatitude(BigDecimal.valueOf(Double.parseDouble(String.valueOf(j.getOrDefault(PositionContant.LATITUDE, BigDecimal.valueOf(0L))))));
            vo.setCarAltitude(BigDecimal.valueOf(Double.parseDouble(String.valueOf(j.getOrDefault(PositionContant.ALTITUDE, BigDecimal.valueOf(0L))))));
            vo.setCarDirection(BigDecimal.valueOf(Double.parseDouble(String.valueOf(j.getOrDefault(PositionContant.DIRECTION, BigDecimal.valueOf(0L))))));
            vo.setCarPlate(carPlate);
            vo.setPlateColor(String.valueOf(j.get(PositionContant.PLATE_COLOR)));
            vo.setCompanyId(car.getCompanyId());
            vo.setCarVelocity(BigDecimal.valueOf(Double.parseDouble(String.valueOf(j.getOrDefault(PositionContant.CAR_VELOCITY, BigDecimal.valueOf(0L))))));
            vo.setUpdateTime(String.valueOf(j.get(PositionContant.UPDATE_TIME)));
            vo.setSelfNo(car.getSelfNo());
            vo.setStatus(Integer.valueOf(String.valueOf(j.getOrDefault(PositionContant.ONLINE_STATUS, car.getStatus()))));
            vo.setMoving(car.getMoving());
            vo.setVehicleType(car.getVehicleType());
            if (Objects.nonNull(car.getVehicleType())) {
                String vehicleTypeName = VehicleTypeEnum.getDescByCode(car.getVehicleType());
                vo.setVehicleTypeName(vehicleTypeName);
            }
            vo.setLimitSpeed(VehicleLimitSpeedEnum.getLimitSpeedByCode(car.getVehicleType()));
            if (Objects.nonNull(car.getCompanyId())) {
                CompanyDTO vehicleCompany = companiesCacheHelper.getById(car.getCompanyId());
                vo.setCompanyName(vehicleCompany.getName());
            }
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 判断日期有没有跨月
     */
    private void checkDateExtendIntoNextDay(LocalDateTime startTime, LocalDateTime endTime) {
        if (!NdDateUtils.format(startTime, DatePattern.NORM_DATE_PATTERN)
                .equals(NdDateUtils.format(endTime, DatePattern.NORM_DATE_PATTERN))) {
            throw new BizException(ExceptionCode.OPERATION_EX.getCode(), "由于GPS数据量大 不支持跨日查询!");
        }
    }

    private Boolean checkIsOverSpeed(VehiclePositionInfo positionInfo, NdVehicleBaseInfo car) {
        //混凝土和移动泵车 是50
        Integer vehicleType = car.getVehicleType();
        VehicleTypeEnum enumByValue = VehicleTypeEnum.getEnumByValue(vehicleType);
        assert enumByValue != null;
        if(enumByValue.equals(VehicleTypeEnum.OTHER)){
            if(Objects.nonNull(positionInfo.getDashboardSpeed()) &&
                    positionInfo.getCarVelocity().compareTo(BigDecimal.valueOf(80)) >= 0){
                return Boolean.TRUE;
            }
        }else if(enumByValue.equals(VehicleTypeEnum.KRHP)){
            if(Objects.nonNull(positionInfo.getDashboardSpeed()) &&
                    positionInfo.getCarVelocity().compareTo(BigDecimal.valueOf(50)) >= 0) {
                return Boolean.TRUE;
            }
        }else if(enumByValue.equals(VehicleTypeEnum.MIXER)){
            if(Objects.nonNull(positionInfo.getDashboardSpeed()) &&
                    positionInfo.getCarVelocity().compareTo(BigDecimal.valueOf(50)) >= 0) {
                return Boolean.TRUE;
            }
        }else if(enumByValue.equals(VehicleTypeEnum.BULK_CEMENT_TRUCK)){
            if(Objects.nonNull(positionInfo.getDashboardSpeed()) &&
                    positionInfo.getCarVelocity().compareTo(BigDecimal.valueOf(80)) >= 0) {
                return Boolean.TRUE;
            }
        }else if(enumByValue.equals(VehicleTypeEnum.READY_MIXED_MORTAR_TRUC)){
            if(Objects.nonNull(positionInfo.getDashboardSpeed()) &&
                    positionInfo.getCarVelocity().compareTo(BigDecimal.valueOf(80)) >= 0) {
                return Boolean.TRUE;
            }
        }

        return Boolean.FALSE;
    }

    private void checkContinuousOverspeed(List<ResTrajectoryDataVO> resTrajectoryDataVOList) {
        if(CollUtil.isNotEmpty(resTrajectoryDataVOList)){
            for(int i = 0;i < resTrajectoryDataVOList.size();i++){
                ResTrajectoryDataVO vo = resTrajectoryDataVOList.get(i);
                String carPlate = vo.getCarPlate();
                String REDIS_CAR_FILED = "car-gps:";
                if(vo.getIsOverSpeed()){
                    Object num = redisTemplate.opsForValue().get(carPlate);
                    //如果之前已经有连续6个了 那就相安无事
                    if(Objects.isNull(num)){
                        redisTemplate.opsForValue().set(REDIS_CAR_FILED + carPlate,1,500, TimeUnit.SECONDS);
                    }else{
                        redisTemplate.opsForValue().increment(REDIS_CAR_FILED + carPlate);
                    }
                }else{
                    Object num = redisTemplate.opsForValue().get(REDIS_CAR_FILED + carPlate);
                    if(Objects.isNull(num)){
                        redisTemplate.opsForValue().set(REDIS_CAR_FILED + carPlate,0,500,TimeUnit.SECONDS);
                        continue;
                    }
                    //如果之前已经有连续6个了 那就相安无事
                    int overNum = Integer.parseInt(String.valueOf(num));
                    if(overNum >= 6){
                        redisTemplate.opsForValue().set(REDIS_CAR_FILED + carPlate,0,500,TimeUnit.SECONDS);
                    }else{
                        //如果不是的话 要把之前的 置为不超速
                        changeOverspeedFalse(resTrajectoryDataVOList,i,vo,overNum);
                    }
                }
            }
        }
    }

    private void changeOverspeedFalse(List<ResTrajectoryDataVO> resTrajectoryDataVOList,int index, ResTrajectoryDataVO vo, Integer overNum) {
        int flagNum = overNum;
        for(int i = index;i >= 0;i--){
            if(flagNum <= 0){
                break;
            }
            ResTrajectoryDataVO innerVo = resTrajectoryDataVOList.get(i);
            if(innerVo.getCarPlate().equals(vo.getCarPlate())){
                innerVo.setIsOverSpeed(Boolean.FALSE);
                flagNum--;
            }
        }
    }

}
