package com.ningdatech.carapi.road.task;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.map.MapUtil;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.carapi.road.model.OverspeedPositionInfo;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.carapi.road.constant.*;
import com.ningdatech.carapi.road.model.entity.RoadBehaviorAnalysis;
import com.ningdatech.carapi.road.service.IRoadBehaviorAnalysisService;
import com.ningdatech.carapi.scheduler.contants.TaskConstant;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.StopWatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

/**
 * @author CMM
 * 道路监控实时算法分析数据拉取定时任务
 * @since 2024/10/12 10:39
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class RoadMonitorDataPullTask {

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private Integer redisPort;

    @Value("${spring.redis.password}")
    private String redisPassword;

    @Value("${task.switch.is-open}")
    private boolean flag;

    private final IRoadBehaviorAnalysisService roadBehaviorAnalysisService;

    // 定时更新道路监控实时算法分析数据 每10分钟一次
    @Scheduled(cron = "0 */10 * * * ?")
    public void doTask() {
        if (!flag){
            log.info("道路监控实时算法分析数据拉取定时任务未开启!");
            return;
        }
        log.info("=========== 道路监控实时算法分析数据拉取 ======== 任务开始");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        List<String> dataList;
        Map<String, String> REDIS_KEY_REGION_MAP = getRedisKeyRegionMap();
        try (Jedis jedis = new Jedis(redisHost, redisPort)) {
            jedis.auth(redisPassword);
            dataList = REDIS_KEY_REGION_MAP.keySet().stream().map(jedis::get)
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toList());
        }
        if (CollUtil.isNotEmpty(dataList)) {
            for (String data : dataList) {
                // 解析获取数据
                JSONObject jsonObject = JSON.parseObject(data);
                String time = jsonObject.getString("Time");
                String region = jsonObject.getString("Region");

                // 转换为LocalDateTime类型
                LocalDateTime createTime = LocalDateTimeUtil.parse(time, NdDateUtils.DEFAULT_DATE_TIME_FORMAT);

                // 根据时间 判断数据库中是否存在 不存在 入库
                // 获取当前区域当前数据产生时间 在数据库中是否存入过
                List<RoadBehaviorAnalysis> list = checkDataExists(createTime, region);

                // 如果没有存入过
                if (CollUtil.isEmpty(list)) {
                    List<RoadBehaviorAnalysis> saveDataList = assembleData(jsonObject);
                    if (CollUtil.isNotEmpty(saveDataList)){
                        log.info("=========== 保存数据条数：{}",saveDataList.size());
                        roadBehaviorAnalysisService.saveBatch(saveDataList);
                        log.info("=========== 保存成功");
                    }
                }
            }
        }
        stopWatch.stop();
        log.info("=========== 道路监控实时算法分析数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds());
    }

    @FunctionalInterface
    interface DataAssembler {
        void assemble(JSONArray dataArray, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject);
    }
    private List<RoadBehaviorAnalysis> assembleData(JSONObject jsonObject) {
        List<RoadBehaviorAnalysis> saveDataList = Lists.newArrayList();
        // 定义一个映射，关联JSON数组的键和对应的数据处理方法
        Map<String, DataAssembler> assemblerMap = new HashMap<>();
        assemblerMap.put("Driver", this::assemblyDriverData);
        assemblerMap.put("Speed", this::assemblySpeedData);
        assemblerMap.put("Pedestrian", this::assemblyPedestrianData);
        assemblerMap.put("Road", this::assemblyRoadData);
        assemblerMap.put("Congestion", this::assemblyCongestionData);
        assemblerMap.put("Weather", this::assemblyWeatherData);
        assemblerMap.put("Vehicle", this::assemblyVehicleData);
        assemblerMap.put("Bicycle", this::assemblyBicycleData);
        assemblerMap.put("Offense", this::assemblyOffenseData);
        // 遍历映射，处理每个数据集
        assemblerMap.forEach((key, assembler) -> {
            JSONArray dataArray = jsonObject.getJSONArray(key);
            if (dataArray != null && !dataArray.isEmpty()) {
                assembler.assemble(dataArray, saveDataList, jsonObject);
            }
        });
        return saveDataList;
    }

    private List<RoadBehaviorAnalysis> checkDataExists(LocalDateTime createTime, String region) {
        return roadBehaviorAnalysisService.list(Wrappers.lambdaQuery(RoadBehaviorAnalysis.class)
                .eq(RoadBehaviorAnalysis::getBehaviorTime, createTime)
                .eq(RoadBehaviorAnalysis::getRegion, region));
    }

    private static Map<String, String> getRedisKeyRegionMap() {
        Map<String, String> REDIS_KEY_REGION_MAP = MapUtil.newHashMap();
        REDIS_KEY_REGION_MAP.put(TaskConstant.RedisKey.HS_DY_GO_REDIS_ALL_DATA_KEY, "1");
        REDIS_KEY_REGION_MAP.put(TaskConstant.RedisKey.HS_DY_COME_REDIS_ALL_DATA_KEY, "2");
        REDIS_KEY_REGION_MAP.put(TaskConstant.RedisKey.HS_YW_GO_REDIS_ALL_DATA_KEY, "3");
        REDIS_KEY_REGION_MAP.put(TaskConstant.RedisKey.HS_YW_COME_REDIS_ALL_DATA_KEY, "4");
        REDIS_KEY_REGION_MAP.put(TaskConstant.RedisKey.HL_LX_REDIS_ALL_DATA_KEY, "5");
        REDIS_KEY_REGION_MAP.put(TaskConstant.RedisKey.HL_YW_REDIS_ALL_DATA_KEY, "6");
        return REDIS_KEY_REGION_MAP;
    }

    /**
     * 车辆违章情况
     * @param offense \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyOffenseData(JSONArray offense, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < offense.size(); i++) {
            String offenseString = offense.getString(i);
            if (offenseString.contains("red")){
                offenseString = "Running_a_red_light";
            } else if (offenseString.contains("line")) {
                offenseString = "Crossing_the_line";
            }
            OffenseStatusTypeEnum offenseStatusTypeEnum = OffenseStatusTypeEnum.valueOf(offenseString);
            behaviorBuilder.append(offenseStatusTypeEnum.getDesc());
            if (i != offense.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(offenseStatusTypeEnum.name());
            if (i != offense.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.CAR.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 自行车情况
     * @param bicycle \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyBicycleData(JSONArray bicycle, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < bicycle.size(); i++) {
            String bicycleString = bicycle.getString(i);
            if (bicycleString.contains("Collision")){
                bicycleString = "Bicycle_Collisions";
            } else if (bicycleString.contains("Motorway")) {
                bicycleString = "Motorway_Intrusion";
            }
            BicycleStatusTypeEnum bicycleStatusTypeEnum = BicycleStatusTypeEnum.valueOf(bicycleString);
            behaviorBuilder.append(bicycleStatusTypeEnum.getDesc());
            if (i != bicycle.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(bicycleStatusTypeEnum.name());
            if (i != bicycle.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.ROAD.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 车辆情况
     * @param vehicle \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyVehicleData(JSONArray vehicle, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < vehicle.size(); i++) {
            String vehicleString = vehicle.getString(i);
            if (vehicleString.contains("Collision")){
                vehicleString = "Vehicle_Collisions";
            } else if (vehicleString.contains("Smoke")) {
                vehicleString = "Smoke_or_Fire";
            }
            VehicleStatusTypeEnum vehicleStatusTypeEnum = VehicleStatusTypeEnum.valueOf(vehicleString);
            behaviorBuilder.append(vehicleStatusTypeEnum.getDesc());
            if (i != vehicle.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(vehicleStatusTypeEnum.name());
            if (i != vehicle.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.CAR.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 天气情况
     * @param weather \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyWeatherData(JSONArray weather, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < weather.size(); i++) {
            String weatherString = weather.getString(i);
            WeatherStatusTypeEnum weatherStatusTypeEnum = WeatherStatusTypeEnum.valueOf(weatherString);
            behaviorBuilder.append(weatherStatusTypeEnum.getDesc());
            if (i != weather.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(weatherStatusTypeEnum.name());
            if (i != weather.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.ENVIRONMENT.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 拥堵情况
     * @param congestion \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyCongestionData(JSONArray congestion, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < congestion.size(); i++) {
            String congestionString = congestion.getString(i);
            CongestionStatusTypeEnum congestionStatusTypeEnum = CongestionStatusTypeEnum.valueOf(congestionString);
            behaviorBuilder.append(congestionStatusTypeEnum.getDesc());
            if (i != congestion.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(congestionStatusTypeEnum.name());
            if (i != congestion.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.ROAD.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 道路情况
     * @param road \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyRoadData(JSONArray road, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < road.size(); i++) {
            String roadString = road.getString(i);
            RoadStatusTypeEnum roadStatusTypeEnum = RoadStatusTypeEnum.valueOf(roadString);
            behaviorBuilder.append(roadStatusTypeEnum.getDesc());
            if (i != road.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(roadStatusTypeEnum.name());
            if (i != road.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.ROAD.getCode(),jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 行人情况
     * @param pedestrian \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyPedestrianData(JSONArray pedestrian, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < pedestrian.size(); i++) {
            String pedestrianString = pedestrian.getString(i);
            PedestrianStatusTypeEnum pedestrianStatusTypeEnum = PedestrianStatusTypeEnum.valueOf(pedestrianString);
            behaviorBuilder.append(pedestrianStatusTypeEnum.getDesc());
            if (i != pedestrian.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(pedestrianStatusTypeEnum.name());
            if (i != pedestrian.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.ROAD.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    private RoadBehaviorAnalysis getRoadBehaviorAnalysis(StringBuilder behaviorBuilder, StringBuilder behaviorCodeBuilder, int type, JSONObject jsonObject) {
        String behavior = behaviorBuilder.toString();
        String behaviorCode = behaviorCodeBuilder.toString();

        String time = jsonObject.getString("Time");
        String region = jsonObject.getString("Region");

        String picturePath = jsonObject.getString("PicturePath");
        JSONArray longitudes = jsonObject.getJSONArray("Longitude");
        JSONArray latitudes = jsonObject.getJSONArray("Latitude");

        // 转换为LocalDateTime类型
        LocalDateTime createTime = LocalDateTimeUtil.parse(time, NdDateUtils.DEFAULT_DATE_TIME_FORMAT);

        RoadBehaviorAnalysis roadBehaviorAnalysis = new RoadBehaviorAnalysis();
        roadBehaviorAnalysis.setBehavior(behavior);
        roadBehaviorAnalysis.setBehaviorCode(behaviorCode);
        roadBehaviorAnalysis.setBehaviorTime(createTime);
        roadBehaviorAnalysis.setCreateTime(LocalDateTime.now());
        roadBehaviorAnalysis.setUpdateTime(LocalDateTime.now());
        roadBehaviorAnalysis.setRegion(region);
        roadBehaviorAnalysis.setType(type);
        roadBehaviorAnalysis.setPicturePath(picturePath);
        // 获取超速车辆的经纬度数据
        if ((Objects.nonNull(longitudes) && CollUtil.isNotEmpty(longitudes)) && (Objects.nonNull(latitudes) && CollUtil.isNotEmpty(latitudes))){
            List<OverspeedPositionInfo> positionInfoList = Lists.newArrayList();
            // 按一对一的关系 拼接经纬度
            for (int i = 0; i < longitudes.size(); i++) {
                OverspeedPositionInfo positionInfo = new OverspeedPositionInfo();
                positionInfo.setLongitude(longitudes.getString(i));
                positionInfo.setLatitude(latitudes.getString(i));
                positionInfoList.add(positionInfo);
            }
            // 转化为Json字符串
            roadBehaviorAnalysis.setOverspeedPositionInfo(JSON.toJSONString(positionInfoList));
        }
        roadBehaviorAnalysis.setIsWarn(judgeIsWarn(behaviorCode));
        // 首次保存的数据默认未处理
        roadBehaviorAnalysis.setIsHandled(Boolean.FALSE);
        return roadBehaviorAnalysis;
    }

    /**
     * 判断是否需要预警
     * @param behaviorCode
     * @return
     */
    private Boolean judgeIsWarn(String behaviorCode) {
        // 获取需要预警的behaviorCode
        List<String> needWarnBehaviorCodeList = getNeedWarnBehaviorCode();
        if (StringUtils.isNotBlank(behaviorCode)){
            List<String> behaviorCodeList = Arrays.stream(behaviorCode.split(StrPool.COMMA)).collect(Collectors.toList());
            // 判断是否包含需要预警的behaviorCode
            return behaviorCodeList.stream().anyMatch(needWarnBehaviorCodeList::contains);
        }
        return Boolean.FALSE;
    }

    private List<String> getNeedWarnBehaviorCode() {
        List<String> dataList = Lists.newArrayList();
        // Speed-->Reverse
        dataList.add(SpeedStatusTypeEnum.Reverse.name());
        // Pedestrian-->Trespass、Traverse
        dataList.add(PedestrianStatusTypeEnum.Trespass.name());
        dataList.add(PedestrianStatusTypeEnum.Traverse.name());
        // Road-->Damaged
        dataList.add(RoadStatusTypeEnum.Damaged.name());
        // Congestion-->Heavy
        dataList.add(CongestionStatusTypeEnum.Heavy.name());
        // Vehicle-->Vehicle_Collisions、Overturn、Smoke_or_Fire
        dataList.add(VehicleStatusTypeEnum.Vehicle_Collisions.name());
        dataList.add(VehicleStatusTypeEnum.Overturn.name());
        dataList.add(VehicleStatusTypeEnum.Smoke_or_Fire.name());
        // Bicycle-->Bicycle_Collisions、Motorway_Intrusion
        dataList.add(BicycleStatusTypeEnum.Bicycle_Collisions.name());
        dataList.add(BicycleStatusTypeEnum.Motorway_Intrusion.name());
        return dataList;
    }

    /**
     * 保存车辆速度异常数据
     * @param speed \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblySpeedData(JSONArray speed, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < speed.size(); i++) {
            String speedString = speed.getString(i);
            SpeedStatusTypeEnum speedStatusTypeEnum = SpeedStatusTypeEnum.valueOf(speedString);
            behaviorBuilder.append(speedStatusTypeEnum.getDesc());
            if (i != speed.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(speedStatusTypeEnum.name());
            if (i != speed.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.CAR.getCode(), jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }

    /**
     * 保存驾驶员异常行为数据
     * @param driver \
     * @param saveDataList \
     * @param jsonObject \
     */
    private void assemblyDriverData(JSONArray driver, List<RoadBehaviorAnalysis> saveDataList, JSONObject jsonObject) {
        StringBuilder behaviorBuilder = new StringBuilder();
        StringBuilder behaviorCodeBuilder = new StringBuilder();
        for (int i = 0; i < driver.size(); i++) {
            String driverString = driver.getString(i);
            if (driverString.contains("habits")) {
                driverString = "Poor_habits";
            }
            DriverStatusTypeEnum driverStatusTypeEnum = DriverStatusTypeEnum.valueOf(driverString);
            behaviorBuilder.append(driverStatusTypeEnum.getDesc());
            if (i != driver.size() - 1) {
                behaviorBuilder.append(",");
            }
            behaviorCodeBuilder.append(driverStatusTypeEnum.name());
            if (i != driver.size() - 1) {
                behaviorCodeBuilder.append(",");
            }
        }
        RoadBehaviorAnalysis roadBehaviorAnalysis = getRoadBehaviorAnalysis(behaviorBuilder, behaviorCodeBuilder, RoadBehaviorTypeEnum.DRIVER.getCode(),jsonObject);
        saveDataList.add(roadBehaviorAnalysis);
    }
}
