package com.lutao.cloud.oil.runner;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lutao.cloud.oil.pojo.entity.TagOnlineRate;
import com.lutao.cloud.oil.service.*;
import com.lutao.cloud.oil.util.TimeUtil;
import com.lutao.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 在线率定时执行器
 *
 * @author yx
 * @date 2024/04/08
 */
@Component
@Slf4j
public class OnlineRatingExecutor {

    @Resource(name = "redisTemplateAlias")
    private RedisTemplate redisTemplate;

    @Resource
    private PlatformInfoService platformInfoService;

    @Resource
    private TagOnlineRateService tagOnlineRateService;

    @Resource
    private CollectLogService collectLogService;

    @Resource
    private FireParametersTagService fireParametersTagService;

    @Resource
    private GasParametersTagService gasParametersTagService;

    @Resource
    private HazardousGasParametersTagService hazardousGasParametersTagService;

    @Resource
    private ProcessParameterTagService processParameterTagService;

    /**
     * 0点启动初始化 指标信息
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void initProcess() {
        String currentTime = LocalDateTime.now().toString();
        List<FireParametersTag> fireParametersTags = fireParametersTagService
                .list(Wrappers.lambdaQuery(FireParametersTag.class)
                .isNotNull(FireParametersTag::getTagCode)
                .isNotNull(FireParametersTag::getPlatformCode)
                .isNotNull(FireParametersTag::getAppId)
                .eq(FireParametersTag::getStatus, "0"));
        for (FireParametersTag fireParametersTag : fireParametersTags) {
            BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(LocalDate.now() + ":"
                    + PerceptionMonitorFireInfo.TABLE_NAME
                    + ":" + fireParametersTag.getAppId()
                    + ":" + fireParametersTag.getPlatformCode()
                    + ":" + fireParametersTag.getTagCode());
            boundHashOperations.put("should_accept_count", 0);
            boundHashOperations.put("received_count", 0);
            boundHashOperations.put("begin_time", currentTime);
            boundHashOperations.put("enable", 0);
            boundHashOperations.put("resource",PerceptionMonitorFireInfo.TABLE_NAME);

            boundHashOperations.expire(48, TimeUnit.HOURS);
        }

        List<GasParametersTag> gasParametersTags = gasParametersTagService
                .list(Wrappers.lambdaQuery(GasParametersTag.class)
                .isNotNull(GasParametersTag::getTagCode)
                .isNotNull(GasParametersTag::getPlatformCode)
                .isNotNull(GasParametersTag::getAppId)
                .eq(GasParametersTag::getStatus, "0"));
        for (GasParametersTag gasParametersTag : gasParametersTags) {
            BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(LocalDate.now()
                    + ":" + PerceptionMonitorGasInfo.TABLE_NAME
                    + ":" + gasParametersTag.getAppId()
                    + ":" + gasParametersTag.getPlatformCode()
                    + ":" + gasParametersTag.getTagCode());
            boundHashOperations.put("should_accept_count", 0);
            boundHashOperations.put("received_count", 0);
            boundHashOperations.put("begin_time", currentTime);
            boundHashOperations.put("enable", 0);
            boundHashOperations.put("resource",PerceptionMonitorGasInfo.TABLE_NAME);
            boundHashOperations.expire(48, TimeUnit.HOURS);
        }

        List<HazardousGasParametersTag> hazardousGasParametersTags = hazardousGasParametersTagService
                .list(Wrappers.lambdaQuery(HazardousGasParametersTag.class)
                .isNotNull(HazardousGasParametersTag::getTagCode)
                .isNotNull(HazardousGasParametersTag::getPlatformCode)
                .isNotNull(HazardousGasParametersTag::getAppId)
                .eq(HazardousGasParametersTag::getStatus, "0"));

        for (HazardousGasParametersTag hazardousGasParametersTag : hazardousGasParametersTags) {
            BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(LocalDate.now()
                    + ":" + PerceptionMonitorHazardousGasInfo.TABLE_NAME
                    + ":" + hazardousGasParametersTag.getAppId()
                    + ":" + hazardousGasParametersTag.getPlatformCode()
                    + ":" + hazardousGasParametersTag.getTagCode());
            boundHashOperations.put("should_accept_count", 0);
            boundHashOperations.put("received_count", 0);
            boundHashOperations.put("begin_time", currentTime);
            boundHashOperations.put("enable", 0);
            boundHashOperations.put("resource",PerceptionMonitorHazardousGasInfo.TABLE_NAME);
            boundHashOperations.expire(48, TimeUnit.HOURS);
        }

        List<ProcessParameterTag> processParameterTags = processParameterTagService
                .list(Wrappers.lambdaQuery(ProcessParameterTag.class)
                .isNotNull(ProcessParameterTag::getTagCode)
                .isNotNull(ProcessParameterTag::getPlatformCode)
                .isNotNull(ProcessParameterTag::getAppId)
                .eq(ProcessParameterTag::getStatus, "0"));
        for (ProcessParameterTag processParameterTag : processParameterTags) {
            BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(LocalDate.now()
                    + ":" + PerceptionMonitorInfo.TABLE_NAME
                    + ":" + processParameterTag.getAppId()
                    + ":" + processParameterTag.getPlatformCode()
                    + ":" + processParameterTag.getTagCode());
            boundHashOperations.put("should_accept_count", 0);
            boundHashOperations.put("received_count", 0);
            boundHashOperations.put("begin_time", currentTime);
            boundHashOperations.put("enable", 0);
            boundHashOperations.put("resource",PerceptionMonitorInfo.TABLE_NAME);
            boundHashOperations.expire(48, TimeUnit.HOURS);
        }


    }

    //@Scheduled(cron = "0 * * * * ?")
    public void test(){
        log.info("开始执行");
        BoundHashOperations perceptionMonitorFireInfoCache = redisTemplate.boundHashOps("2024-08-28:perception_monitor_fire_info:zhonghaiyou:ZHY-BZ28-1N:BZ28-1N.DCS.FZ017_LEVEL2_FIRE.F_CV");
        Integer enable = (Integer) perceptionMonitorFireInfoCache.get("enable");
        log.info(String.valueOf(enable));
        if (enable != null && enable == 0){
            try {
                // 获取当前时间字符串
                String currentTimeStr = LocalDateTime.now().toString();
                // 获取已接收计数
                Integer receivedCount = (Integer) perceptionMonitorFireInfoCache.get("received_count");
                log.info("receivedCount:" + receivedCount);
                if (Objects.isNull(receivedCount) || receivedCount < 1) {
                    // 初始化缓存条目
                    perceptionMonitorFireInfoCache.put("received_time", currentTimeStr);
                    perceptionMonitorFireInfoCache.put("entity", currentTimeStr);
                    perceptionMonitorFireInfoCache.put("resource", "perception_monitor_fire_info");
                    perceptionMonitorFireInfoCache.increment("received_count", 1);
                    perceptionMonitorFireInfoCache.put("value", currentTimeStr);
                    // 设置过期时间
                    perceptionMonitorFireInfoCache.expire(48, TimeUnit.HOURS);
                } else {
                    // 检查是否需要更新
                    String lastReceivedTime = (String) perceptionMonitorFireInfoCache.get("received_time");
                    String currentValue = (String) perceptionMonitorFireInfoCache.get("value");
                    String newRtValue = currentTimeStr;
                    log.info("lastReceivedTime:" + lastReceivedTime);
                    log.info("currentValue:" + currentValue);
                    if (!currentTimeStr.equals(lastReceivedTime) && currentValue != null && !currentValue.equals(newRtValue)) {
                        perceptionMonitorFireInfoCache.increment("received_count", 1);
                        // 计算每分钟应该接受的数据量
                        LocalDateTime beginTime = LocalDateTime.parse(perceptionMonitorFireInfoCache.get("begin_time").toString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
                        Long shouldAcceptCount = TimeUtil.getTimeDifference(beginTime, LocalDateTime.now());
                        perceptionMonitorFireInfoCache.put("should_accept_count", shouldAcceptCount);
                        if (shouldAcceptCount>0){
                            perceptionMonitorFireInfoCache.put("online_rate",String.valueOf((float) receivedCount / shouldAcceptCount));
                        }
                        // 更新缓存条目
                        perceptionMonitorFireInfoCache.put("entity", JSONUtil.toJsonStr("perceptionMonitorFireInfo"));
                        perceptionMonitorFireInfoCache.put("received_time", currentTimeStr);
                        perceptionMonitorFireInfoCache.put("value", newRtValue);
                    }
                }
            } catch (Exception e) {
                log.error("更新感知监控火气信息缓存异常", e);
            }
        }
    }


    /**
     * 在线率定时任务
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void execute() {
        try {
            long startTime = System.currentTimeMillis();

            Set<String> redisKeys = redisTemplate.keys(LocalDate.now().minusDays(1) + "*");
            List<PlatformInfo> platformInfos = platformInfoService.list();

            Map<String, PlatformInfo> platformMap = new HashMap<>();
            for (PlatformInfo platformInfo : platformInfos) {
                platformMap.put(platformInfo.getPlatformCode(), platformInfo);
            }
            if (redisKeys == null) {
                return;
            }
            for (String key : redisKeys) {
                processKey(key, platformMap);
            }
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime; //milliseconds
            String executionTimeStr = String.format("%d milliseconds", executionTime);
            CollectLog collectLog = new CollectLog();
            collectLog.setCommand("tag_online_rate");
            collectLog.setData(executionTimeStr);
            collectLogService.save(collectLog);

        } catch (Exception e) {
            CollectLog collectLog = new CollectLog();
            collectLog.setCommand("error");
            collectLog.setData("tag_online_rate==error==" + e.getMessage());
            e.printStackTrace();
            collectLogService.save(collectLog);
        }
    }

    private void processKey(String key, Map<String, PlatformInfo> platformMap) {

        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(key);
        String resource = (String) boundHashOperations.get("resource");
        Integer receivedCount = Optional.ofNullable((Integer) boundHashOperations.get("received_count")).orElse(0);
        String entity = (String) boundHashOperations.get("entity");
        LocalDateTime beginTime = parseDateTime((String) boundHashOperations.get("begin_time"));
        LocalDateTime endTime = parseDateTime((String) boundHashOperations.get("end_time"));
        Integer shouldAcceptCount = Optional.ofNullable((Integer) boundHashOperations.get("should_accept_count")).orElse(0);

        TagOnlineRate tagOnlineRate = new TagOnlineRate();
        switch (Objects.requireNonNull(resource)) {
            case "perception_monitor_fire_info":
                PerceptionMonitorFireInfo fireInfo = JSONUtil.toBean(entity, PerceptionMonitorFireInfo.class); // 将JSON字符串转换为Java对象

                tagOnlineRate = processFireInfo(fireInfo, tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
                break;
            case "perception_monitor_gas_info":
                PerceptionMonitorGasInfo gasInfo = JSONUtil.toBean(entity, PerceptionMonitorGasInfo.class); // 将JSON字符串转换为Java对象

                tagOnlineRate = processGasInfo(gasInfo, tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
                break;
            case "perception_monitor_hazardous_gas_info":
                PerceptionMonitorHazardousGasInfo hazardousGasInfo = JSONUtil.toBean(entity, PerceptionMonitorHazardousGasInfo.class); // 将JSON字符串转换为Java对象

                tagOnlineRate = processHazardousGasInfo(hazardousGasInfo, tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
                break;
            case "perception_monitor_info":
                PerceptionMonitorInfo monitorInfo = JSONUtil.toBean(entity, PerceptionMonitorInfo.class); // 将JSON字符串转换为Java对象

                tagOnlineRate = processMonitorInfo(monitorInfo, tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
                break;
            default:
                break;
        }

        tagOnlineRateService.save(tagOnlineRate);
    }

    private LocalDateTime parseDateTime(String dateTimeStr) {
        List<String> formats = Arrays.asList(
                "yyyy-MM-dd'T'HH:mm:ss",
                "yyyy-MM-dd'T'HH:mm:ss.SSS"
        );
        LocalDateTime dateTime = null;
        if (dateTimeStr != null) {
            for (String format : formats) {
                try {
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
                    dateTime = LocalDateTime.parse(dateTimeStr, formatter);
                    break;
                } catch (DateTimeParseException e) {
                    // Ignore and try the next format
                }
            }
        }
        return dateTime;
    }

    private TagOnlineRate processFireInfo(PerceptionMonitorFireInfo fireInfo, TagOnlineRate tagOnlineRate, Map<String, PlatformInfo> platformMap, Integer receivedCount, LocalDateTime beginTime, LocalDateTime endTime, String resource,Integer shouldAcceptCount) {
        if (fireInfo != null) {
            return fillTagOnlineRate(fireInfo.getAppId(), fireInfo.getPlatformCode(), fireInfo.getTagId(), fireInfo.getTagCode(), fireInfo.getTagName(), tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
        } else return null;
    }

    private TagOnlineRate processGasInfo(PerceptionMonitorGasInfo gasInfo, TagOnlineRate tagOnlineRate, Map<String, PlatformInfo> platformMap, Integer receivedCount, LocalDateTime beginTime, LocalDateTime endTime, String resource,Integer shouldAcceptCount) {
        if (gasInfo != null) {
            return fillTagOnlineRate(gasInfo.getAppId(), gasInfo.getPlatformCode(), gasInfo.getTagId(), gasInfo.getTagCode(), gasInfo.getTagName(), tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
        } else return null;
    }

    private TagOnlineRate processHazardousGasInfo(PerceptionMonitorHazardousGasInfo hazardousGasInfo, TagOnlineRate tagOnlineRate, Map<String, PlatformInfo> platformMap, Integer receivedCount, LocalDateTime beginTime, LocalDateTime endTime, String resource,Integer shouldAcceptCount) {
        if (hazardousGasInfo != null) {
            return fillTagOnlineRate(hazardousGasInfo.getAppId(), hazardousGasInfo.getPlatformCode(), hazardousGasInfo.getTagId(), hazardousGasInfo.getTagCode(), hazardousGasInfo.getTagName(), tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
        } else return null;
    }

    private TagOnlineRate processMonitorInfo(PerceptionMonitorInfo monitorInfo, TagOnlineRate tagOnlineRate, Map<String, PlatformInfo> platformMap, Integer receivedCount, LocalDateTime beginTime, LocalDateTime endTime, String resource,Integer shouldAcceptCount) {
        if (monitorInfo != null) {
            return fillTagOnlineRate(monitorInfo.getAppId(), monitorInfo.getPlatformCode(), monitorInfo.getTagId(), monitorInfo.getTagCode(), monitorInfo.getTagName(), tagOnlineRate, platformMap, receivedCount, beginTime, endTime, resource,shouldAcceptCount);
        } else return null;
    }


    private TagOnlineRate fillTagOnlineRate(String appId, String platformCode, String tagId, String tagCode, String tagName, TagOnlineRate tagOnlineRate, Map<String, PlatformInfo> platformMap, Integer receivedCount, LocalDateTime beginTime, LocalDateTime endTime, String resource,Integer shouldAcceptCount) {
        tagOnlineRate.setAppId(appId);
        tagOnlineRate.setPlatformCode(platformCode);
        PlatformInfo platformInfo = platformMap.get(platformCode);
        if (platformInfo != null) {
            tagOnlineRate.setOilGasName(platformMap.get(platformCode).getOilGasName());
            tagOnlineRate.setPlatformName(platformMap.get(platformCode).getPlatformName());
            tagOnlineRate.setOperatingCompany(platformMap.get(platformCode).getOperatingCompany());
        }
        tagOnlineRate.setTagId(tagId);
        tagOnlineRate.setTagCode(tagCode);
        tagOnlineRate.setTagName(tagName);
        tagOnlineRate.setBeginTime(beginTime);
        tagOnlineRate.setEndTime(endTime);
        tagOnlineRate.setReceiveCount(receivedCount);
        tagOnlineRate.setShouldReceiveCount(shouldAcceptCount);
        if (shouldAcceptCount == 0){
            shouldAcceptCount = 143;
        }
        tagOnlineRate.setOnlineRate(receivedCount != null ? String.valueOf((float) receivedCount / shouldAcceptCount) : "0");
        tagOnlineRate.setResourceTable(resource);
        tagOnlineRate.setCreateTime(LocalDateTime.now());
        tagOnlineRate.setUpdateTime(LocalDateTime.now());
        tagOnlineRate.setDataDate(LocalDate.now().minusDays(1).toString());
        return tagOnlineRate;
    }

}
