package com.jiayue.insu.incloud.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiayue.insu.incloud.entity.ForecastData;
import com.jiayue.insu.incloud.entity.PowerGeneration;
import com.jiayue.insu.incloud.entity.PowerStationStatusData;
import com.jiayue.insu.incloud.mapper.PowerGenerationMapper;
import com.jiayue.insu.incloud.service.ForecastDataService;
import com.jiayue.insu.incloud.service.PowerGenerationService;
import com.jiayue.insu.incloud.service.PowerStationStatusDataService;
import com.jiayue.insu.incloud.utils.FixedCapacityList;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 发电量预测业务实现
 *
 * @author yh
 * @version 1.0
 * @since 2022/5/22 11:29
 */
@Service
@Slf4j
public class PowerGenerationServiceImpl extends ServiceImpl<PowerGenerationMapper, PowerGeneration> implements PowerGenerationService {

    @Autowired
    private ForecastDataService forecastDataService;
    @Autowired
    private PowerStationStatusDataService powerStationStatusDataService;

    private static BigDecimal FIFTEEN_MINUTE = BigDecimal.valueOf(15 * 60 * 1000L);
    private static BigDecimal ONE_DAY = BigDecimal.valueOf(24 * 60 * 60 * 1000L);
    private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private final BigDecimal ForecastRadio = new BigDecimal(0.25);
    private final BigDecimal RealValueRadio = new BigDecimal(0.25);


    /**
     * 计算预测发电量
     *
     * @param nowDate
     * @return
     */
    @Override
    public Boolean generatePower(LocalDateTime nowDate, String stationCode) {

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

        String dateStr = simpleDateFormat.format(new Date());

        Date date = null;
        try {
            date = simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        // 查询是否今天生成的发电量
        List<PowerGeneration> checkList = findByGenDate(date,stationCode);

        if (checkList.size() > 0 ){
            log.info("已有当日生成的发电量，不再进行计算！");
            return true;
        }

        // 获取今天的日期部分
        LocalDate today = nowDate.toLocalDate();

        LocalDateTime localDateTime = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        // 获取去年的今天
        LocalDate lastYearToday = today.minusYears(1);
        // 获取当前时间部分
        LocalTime currentTime = nowDate.toLocalTime();
        // 组合去年的日期和当前的时间来创建 LocalDateTime 对象
        LocalDateTime lastYearTodaySameTime = lastYearToday.atTime(currentTime);

        Long startTime = getDayStartTime(System.currentTimeMillis());

        Long endTime = startTime + 12 * ONE_DAY.longValue() - 1;
        // 查询预测数据
        List<ForecastData> forecastDataList = forecastDataService.findByInitTimeAndStationCodeAndForecastTimeBetween(localDateTime, stationCode, startTime, endTime);

        List<PowerGeneration> powerGenerationList = new ArrayList<>();

        FixedCapacityList<BigDecimal> fixedCapacityList = new FixedCapacityList<>(10);

        if (forecastDataList.size() > 0) {
            for (int i = 0; i <= 31; i++) {
                PowerGeneration powerGeneration = new PowerGeneration();
                String forecastDay = DateFormatUtils.format(DateUtils.addDays(new Date(startTime), i), "yyyyMMdd");
                // 电量和
                BigDecimal sumValue = BigDecimal.ZERO;
                // 一天一天数据进行过滤
                String[] datas = findSendData(startTime + ONE_DAY.longValue() * (i), startTime + ONE_DAY.longValue() * (i+1) - FIFTEEN_MINUTE.longValue(), forecastDataList);
                log.info("预测数据 {} {} - {}", datas.length, formatter.format(startTime + ONE_DAY.longValue() * (i)), formatter.format(startTime + ONE_DAY.longValue() * (i+1) - FIFTEEN_MINUTE.longValue()));
                // 数据为空 查找去年相同时间点的实际功率
                if (ObjectUtil.isEmpty(datas)) {
                    // 查询去年同期（因为外网回传文件genDate是后一天的时间）
                    long lastYear = DateUtils.addYears(new Date(startTime + ONE_DAY.longValue() * (i)), -1).getTime() + 24 * 3600 * 1000L;
                    // 查询去年相同时间点实际功率
                    List<PowerStationStatusData> powerStationStatusDataList = powerStationStatusDataService.findByGenDate(lastYear, stationCode);
                    // 查询实际功率开始时间
                    Long queryRealValueStartTime = new Date(startTime + ONE_DAY.longValue() * (i)).getTime();

                    datas = findRealSendData(queryRealValueStartTime, queryRealValueStartTime + ONE_DAY.longValue() - 1, powerStationStatusDataList);

                    if (ObjectUtil.isEmpty(datas)) {
                        // 去年同期实际功率无数据，查询就近实际功率
                        int missDayNum = 31 - i;
                        // 查询实际功率时间
                        Long beforeStartTime = new Date(startTime - ONE_DAY.longValue() * missDayNum).getTime();

                        System.out.println(new Date(beforeStartTime));

                        powerStationStatusDataList = powerStationStatusDataService.findByGenDate(beforeStartTime+24*60*60*1000L, stationCode);

                        datas = findRealSendData(beforeStartTime, beforeStartTime + ONE_DAY.longValue() - 1, powerStationStatusDataList);

                    } else {
                        log.info("同期实际功率数据 {} {} - {}", datas.length, formatter.format(lastYearTodaySameTime.getSecond() + ONE_DAY.longValue() * (i - 1)), formatter.format(lastYearTodaySameTime.getSecond() + ONE_DAY.longValue() * i - FIFTEEN_MINUTE.longValue()));
                    }
                    if (datas.length > 0) {
                        BigDecimal value;
                        for (String data : datas) {
                            if (StrUtil.isNotEmpty(data)) {
                                if (datas.length > 96) {
                                    value = new BigDecimal(data).multiply(RealValueRadio).setScale(2, RoundingMode.HALF_UP);
                                } else {
                                    value = new BigDecimal(data).multiply(ForecastRadio).setScale(2, RoundingMode.HALF_UP);
                                }
                                sumValue = sumValue.add(value);
                            }
                        }
                        fixedCapacityList.push(sumValue);
                    } else {
                        FixedCapacityList<BigDecimal> capacityList = new FixedCapacityList<>(fixedCapacityList.size());
                        capacityList.addAll(fixedCapacityList);
                        if (capacityList.size() > 2) { // 减少极端值
                            capacityList.removeFirstOccurrence(CollUtil.max(fixedCapacityList));
                            capacityList.removeLastOccurrence(CollUtil.min(fixedCapacityList));
                        }
                        log.info("生成数据 {} {} - {}", datas.length, formatter.format(startTime + ONE_DAY.longValue() * (i - 1)), formatter.format(startTime + ONE_DAY.longValue() * i - FIFTEEN_MINUTE.longValue()));
                        int n = capacityList.size();
                        BigDecimal avgValue = avg(fixedCapacityList);
                        BigDecimal lastValue = avg(capacityList.subList(n > 3 ? (n - 3) : n - 1, n - 1));
                        //
                        if (CompareUtil.compare(lastValue, avgValue) < 0) {
                            sumValue = lastValue.add(lastValue.multiply(RandomUtil.randomBigDecimal(BigDecimal.valueOf(0.01), BigDecimal.valueOf(0.05))));
                        } else if (CompareUtil.compare(lastValue, avgValue) > 0) {
                            sumValue = lastValue.add(lastValue.multiply(RandomUtil.randomBigDecimal(BigDecimal.valueOf(-0.25), BigDecimal.valueOf(-0.1))));
                        } else {
                            sumValue = lastValue.add(lastValue.multiply(RandomUtil.randomBigDecimal(BigDecimal.valueOf(-0.02), BigDecimal.valueOf(0.02))));
                        }
                        sumValue = CompareUtil.compare(BigDecimal.ZERO, sumValue) >= 0 ? BigDecimal.ZERO : sumValue;
                        //
                        capacityList.clear();
                    }
                }else {
                    for (String data : datas) {
                        BigDecimal value;
                        if (StrUtil.isNotEmpty(data)) {
                            if (datas.length > 96) {
                                value = new BigDecimal(data).multiply(RealValueRadio).setScale(2, RoundingMode.HALF_UP);
                            } else {
                                value = new BigDecimal(data).multiply(ForecastRadio).setScale(2, RoundingMode.HALF_UP);
                            }
                            sumValue = sumValue.add(value);
                        }
                    }
                    fixedCapacityList.push(sumValue);
                }
                powerGeneration.setGenDate(new Date());
                powerGeneration.setStationCode(stationCode);
                powerGeneration.setPredictedElectricityConsumption(sumValue.setScale(2,RoundingMode.HALF_UP).doubleValue());
                powerGeneration.setForecastTime(forecastDay);
                powerGenerationList.add(powerGeneration);
            }
        }
        this.saveBatch(powerGenerationList);
        return true;
    }

    @Override
    public List<PowerGeneration> findByStationCodeAndInitTime(String stationCode, Date date) {
        LambdaQueryWrapper<PowerGeneration> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PowerGeneration::getGenDate,date);
        lambdaQueryWrapper.eq(PowerGeneration::getStationCode,stationCode);
        return this.list(lambdaQueryWrapper);
    }


    @Transactional(propagation = Propagation.REQUIRED)
    public String[] findSendData(Long stTime, Long eTime, List<ForecastData> list) {
        String[] datas = new String[0];
        List<ForecastData> collect = list.stream().filter(s -> s.getForecastTime() >= stTime && s.getForecastTime() <= eTime).collect(Collectors.toList());
        if (!collect.isEmpty()) {
            datas = new String[collect.size()];
            if (null != collect && collect.size() > 0) {
                int index = 0;
                for (ForecastData f : collect) {
                    datas[index] = String.valueOf(f.getFpValue());
                    index++;
                }
            }
        }
        return datas;
    }


    @Transactional(propagation = Propagation.REQUIRED)
    public String[] findRealSendData(Long stTime, Long eTime, List<PowerStationStatusData> list) {
        String[] datas = new String[0];
        List<PowerStationStatusData> collect = list.stream().filter(s -> s.getForecastTime() >= stTime && s.getForecastTime() <= eTime).collect(Collectors.toList());
        if (!collect.isEmpty()) {
            datas = new String[collect.size()];
            if (null != collect && collect.size() > 0) {
                int index = 0;
                for (PowerStationStatusData f : collect) {
                    datas[index] = String.valueOf(f.getRealValue());
                    index++;
                }
            }
        }
        return datas;
    }

    private BigDecimal avg(List<BigDecimal> bs) {
        BigDecimal value = BigDecimal.ZERO;
        for (BigDecimal b : bs) {
            value = value.add(b);
        }
        BigDecimal avgValue = null;
        try {
            avgValue = value.divide(new BigDecimal(bs.size()), 4, RoundingMode.HALF_UP);
        } catch (Exception ex) {
            log.error(ex.getLocalizedMessage(), ex.getCause());
        }
        return avgValue;
    }

    List<PowerGeneration> findByGenDate(Date genDate,String stationCode){
        LambdaQueryWrapper<PowerGeneration> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PowerGeneration::getGenDate,genDate);
        lambdaQueryWrapper.eq(PowerGeneration::getStationCode,stationCode);
        return list(lambdaQueryWrapper);
    }

    /**
     * 获取指定时间所在天的0点0分
     *
     * @param dateTime 时间毫秒
     * @return 0点0分的毫秒
     */
    public static long getDayStartTime(Long dateTime) {
        Calendar date = Calendar.getInstance();
        date.setTimeInMillis(dateTime);
        date.set(Calendar.HOUR_OF_DAY, 0);
        date.set(Calendar.MINUTE, 0);
        date.set(Calendar.SECOND, 0);
        date.set(Calendar.MILLISECOND, 0);
        return date.getTimeInMillis();
    }
}
