package com.ningdatech.carapi.gps.task;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.ningdatech.cache.model.cache.CacheKey;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.carapi.homepage.entity.model.NdDataAccessGps;
import com.ningdatech.carapi.homepage.service.IDataAccessGpsService;
import com.ningdatech.carapi.scheduler.contants.TaskContant;
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.JSONObject;
import com.ningdatech.carapi.gps.manage.GpsDataPullManage;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.StopWatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author CMM
 * GPS增量数据拉取定时任务
 * @since 2024/10/12 10:39
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class GpsFullDataPullTask {

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

    @Value("${task.gps-data-pull.domain}")
    private String domain;

    @Value("${task.gps-data-pull.real-time-data-url}")
    private String realTimeDataUrl;

    @Value("${task.gps-data-pull.key}")
    private String gpsDataPullKey;

    private final GpsDataPullManage gpsDataPullManage;
    private final IDataAccessGpsService dataAccessGpsService;
    private final CachePlusOps cachePlusOps;

    // 定时增量更新车辆GPS数据 每1分钟一次
    @Scheduled(cron = "0 */1 * * * ?")
    public void doTask() throws Exception {
        if (!flag){
            log.info("GPS增量数据拉取定时任务未开启!");
            return;
        }
        log.info("=========== GPS增量数据拉取 ======== 任务开始");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        gpsDataPullManage.trustAllCertificates();

        // 从缓存中获取业务数据最大id
        CacheKey key = new CacheKey(TaskContant.RedisKey.GPS_DATA_PULL_MAX_BIZ_ID);
        Long id = cachePlusOps.get(key);
        log.info("从缓存中获取业务数据最大id：{}",id);
        // 如果不存在 从表中获取
        if (Objects.isNull(id)){
            id = dataAccessGpsService.getMaxBizId();
        }
        if (Objects.isNull(id)){
            log.error("业务数据最大id为空！");
            return;
        }
        Map<String, String> requestParams = assemblyParams(gpsDataPullKey, id);
        String requestUrl = domain + realTimeDataUrl;
        String response = gpsDataPullManage.sendPostRequest(requestUrl, requestParams);
        if (response == null) {
            log.error("下载路径请求失败！");
            return;
        }
        JSONObject object = JSONObject.parseObject(response);
        log.info("Response from server: {}", object);
        String downloadUrl = object.getString("data");
        if (StringUtils.isBlank(downloadUrl)){
            log.error("下载路径请求失败！");
            return;
        }
        String downloadPath = domain + downloadUrl;
        String result = gpsDataPullManage.sendDownloadRequest(downloadPath);
        if (result == null){
            log.error("文件下载失败！");
            return;
        }
        List<NdDataAccessGps> dataList = JSON.parseArray(result, JSONObject.class).stream().map(jsonObject -> {
            String vehicleNo = jsonObject.getString("vehicle_no");
            Integer color = jsonObject.getInteger("color");
            // 纬度
            String longitude = jsonObject.getString("lon");
            // 经度
            String latitude = jsonObject.getString("lat");
            String direction = jsonObject.getString("direction");
            String gpsSpeed = jsonObject.getString("gps_speed");
            Long gpsTime = jsonObject.getLong("gps_time");
            String bizId = jsonObject.getString("id");
            NdDataAccessGps dataAccessGps = new NdDataAccessGps();
            dataAccessGps.setCarPlate(vehicleNo);
            dataAccessGps.setPlateColor(color.toString());
            dataAccessGps.setCarLongitude(BigDecimal.valueOf(Double.parseDouble(latitude)));
            dataAccessGps.setCarLatitude(BigDecimal.valueOf(Double.parseDouble(longitude)));
            dataAccessGps.setCarDirection(BigDecimal.valueOf(Double.parseDouble(direction)));
            dataAccessGps.setCarVelocity(BigDecimal.valueOf(Double.parseDouble(gpsSpeed)));
            // 将毫秒时间戳转换为Instant对象
            Instant instant = Instant.ofEpochMilli(gpsTime);
            // 使用北京时间时区转换为LocalDateTime
            LocalDateTime localDateTime = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
            dataAccessGps.setUpdateTime(localDateTime);
            dataAccessGps.setCreateTime(LocalDateTime.now());
            dataAccessGps.setBizId(bizId);
            return dataAccessGps;
        }).collect(Collectors.toList());
        if (CollUtil.isEmpty(dataList)){
            log.info("数据为空！");
            return;
        }
        log.info("此次拉取到的数据条数：{}",dataList.size());
        // 获取业务数据最大id
        Long maxBizId = dataList.stream().map(d -> {
            String bizId = d.getBizId();
            return Long.parseLong(bizId);
        }).collect(Collectors.toList()).stream().max(Long::compare).get();
        // 保存到缓存中
        cachePlusOps.set(key,maxBizId,false);
        // 批量保存数据 每批保存1万条
        // 分批
        List<List<NdDataAccessGps>> batchList = new ArrayList<>(ListUtil.partition(new ArrayList<>(dataList), 10000));
        // 创建线程池 线程数是 分批的数量
        ExecutorService executorService = Executors.newFixedThreadPool(batchList.size());
        // 线程计数器,就是 分批的数
        CountDownLatch countDownLatch = new CountDownLatch(batchList.size());
        batchList.forEach(e -> {
            // 每个分批用一个线程执行
            executorService.execute(() -> dataAccessGpsService.saveBatch(e, 10000));
            countDownLatch.countDown();
        });
        // 等待所有线程执行完成
        countDownLatch.await();
        executorService.shutdown();
        executorService.awaitTermination(5, TimeUnit.MINUTES);
        if (executorService.isTerminated()) {
            log.info("子线程执行完毕");
            log.info("主线程开始");
        }
        stopWatch.stop();
        log.info("=========== GPS实时数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds());
    }

    private Map<String, String> assemblyParams(String key, Long id) {
        long timeNow = System.currentTimeMillis() / 1000;
        String token = gpsDataPullManage.generateMD5Token(timeNow, key);

        Map<String, String> params = new HashMap<>();
        params.put("timeNow", String.valueOf(timeNow));
        params.put("token", token);
        params.put("id", String.valueOf(id));
        return params;
    }

}
