package com.cpp.web.service.datafactory;

import com.cpp.common.utils.spring.SpringUtils;
import com.cpp.system.service.ISysConfigService;
import com.cpp.web.domain.station.*;
import com.cpp.web.service.station.*;
import com.cpp.web.utils.DateTimeUtil;
import lombok.Data;

import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class DataStore {


    Map<String, Object> map = new ConcurrentHashMap<>();

    public static final DataStore getInstance() {
        return DataStore.LazyHolder.INSTANCE;
    }

    private static class LazyHolder {
        private static final DataStore INSTANCE = new DataStore();
    }


    public <T> void setData(List<T> t, String stationCode) {
        if (t.size() > 0) {
            Class<?> clazz = t.get(0).getClass();
            if (ForecastPowerShortTermRegulation.class.equals(clazz)) {
                if (this.map.containsKey(stationCode + clazz.getName())) {
                    FixedSizeCollection<T> fixedSizeCollection = ((FixedSizeCollection<T>) this.map.get(stationCode + clazz.getName()));
                    t.forEach(obj -> fixedSizeCollection.add(obj));
                } else {
                    FixedSizeCollection<T> fixedSizeCollection = new FixedSizeCollection<>(960);
                    t.forEach(obj -> fixedSizeCollection.add(obj));
                    this.map.put(stationCode + clazz.getName(), fixedSizeCollection);
                }
            } else if (ForecastPowerUltraShortTermRegulation.class.equals(clazz)) {
                if (this.map.containsKey(stationCode + clazz.getName())) {
                    FixedSizeCollection<T> fixedSizeCollection = ((FixedSizeCollection<T>) this.map.get(stationCode + clazz.getName()));
                    t.forEach(obj -> fixedSizeCollection.add(obj));
                } else {
                    FixedSizeCollection<T> fixedSizeCollection = new FixedSizeCollection<>(16);
                    t.forEach(obj -> fixedSizeCollection.add(obj));
                    this.map.put(stationCode + clazz.getName(), fixedSizeCollection);
                }
            } else if (PowerStationStatusData.class.equals(clazz) || WeatherStationStatusData.class.equals(clazz) || WindTowerStatusData.class.equals(clazz)) {
                this.map.put(stationCode + clazz.getName(), t);
            }
        }
    }


    public <T> T getData(String stationCode, Class<T> clazz) {
        Object o = this.map.get(stationCode + clazz.getName());
        if (o != null) {
            Date date = null;
            if (ForecastPowerShortTermRegulation.class.equals(clazz)) {
                List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulationList = ((FixedSizeCollection<ForecastPowerShortTermRegulation>) o).getList();
                date = DateTimeUtil.getMomentTimeFor15Minute(System.currentTimeMillis());
                Date finalDate1 = date;
                List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulations = forecastPowerShortTermRegulationList.stream().filter(f -> f.getTime().equals(finalDate1)).collect(Collectors.toList());
                if (forecastPowerShortTermRegulations.size() > 0) {
                    return (T) forecastPowerShortTermRegulations.get(0);
                } else {
                    return null;
                }
            } else if (ForecastPowerUltraShortTermRegulation.class.equals(clazz)) {
                List<ForecastPowerUltraShortTermRegulation> forecastPowerUltraShortTermRegulationList = ((FixedSizeCollection<ForecastPowerUltraShortTermRegulation>) o).getList();
                date = DateTimeUtil.getMomentTimeFor15Minute(System.currentTimeMillis());
                Date finalDate = date;
                List<ForecastPowerUltraShortTermRegulation> forecastPowerUltraShortTermRegulations = forecastPowerUltraShortTermRegulationList.stream().filter(f -> f.getTime().equals(finalDate)).collect(Collectors.toList());
                if (forecastPowerUltraShortTermRegulations.size() > 0) {
                    return (T) forecastPowerUltraShortTermRegulations.get(0);
                } else {
                    return null;
                }
            } else if (PowerStationStatusData.class.equals(clazz) || WeatherStationStatusData.class.equals(clazz) || WindTowerStatusData.class.equals(clazz)) {
                return ((List<T>) o).get(0);
            }
        }
        return null;

    }

    public void init() {
        ISysConfigService configService = SpringUtils.getBean(ISysConfigService.class);
        ElectricFieldService electricFieldService = SpringUtils.getBean(ElectricFieldService.class);
        PowerStationStatusDataService powerStationStatusDataService = SpringUtils.getBean(PowerStationStatusDataService.class);
        ForecastPowerShortTermRegulationService forecastPowerShortTermRegulationService = SpringUtils.getBean(ForecastPowerShortTermRegulationService.class);
        ForecastPowerUltraShortTermRegulationService forecastPowerUltraShortTermRegulationService = SpringUtils.getBean(ForecastPowerUltraShortTermRegulationService.class);
        WindTowerStatusDataService windTowerStatusDataService = SpringUtils.getBean(WindTowerStatusDataService.class);
        WeatherStationStatusDataService weatherStationStatusDataService = SpringUtils.getBean(WeatherStationStatusDataService.class);

        List<ElectricField> electricFieldList = electricFieldService.list();
        Date dayStartTime = DateTimeUtil.getDayStartTime(System.currentTimeMillis());
        //不清楚短期是否解析过，多查询一天直接置入
        Date dayEndTime = DateTimeUtil.getDayLastTime(System.currentTimeMillis() + 86400000L);
        if (electricFieldList.size() > 0) {
            int dqAgo = 1;
            try {
                String dqHowLongAgo = configService.selectConfigByKey("dqHowLongAgo");
                dqAgo = Integer.parseInt(dqHowLongAgo);
            } catch (Exception e) {
            }
            int cdqAgo = 1;
            try {
                String cdqHowLongAgo = configService.selectConfigByKey("cdqHowLongAgo");
                cdqAgo = Integer.parseInt(cdqHowLongAgo);
            } catch (Exception e) {
            }

            Map<String, List<PowerStationStatusData>> powerStationStatusDataListMap = powerStationStatusDataService.findByTimeBetween(dayStartTime, dayEndTime).stream().collect(Collectors.groupingBy(PowerStationStatusData::getStationCode, Collectors.toList()));
            Map<String, List<ForecastPowerShortTermRegulation>> forecastPowerShortTermRegulationListMap = forecastPowerShortTermRegulationService.findByForecastTimeBetweenAndHowLongAgo(dayStartTime, dayEndTime, dqAgo).stream().collect(Collectors.groupingBy(ForecastPowerShortTermRegulation::getStationCode, Collectors.toList()));
            Map<String, List<ForecastPowerUltraShortTermRegulation>> forecastPowerUltraShortTermRegulationListMap = forecastPowerUltraShortTermRegulationService.findByForecastTimeBetweenAndHowLongAgo(dayStartTime, dayEndTime, cdqAgo).stream().collect(Collectors.groupingBy(ForecastPowerUltraShortTermRegulation::getStationCode, Collectors.toList()));
            Map<String, List<WindTowerStatusData>> windTowerStatusDataListMap = windTowerStatusDataService.findByBetweenTimeAndStationCode(dayStartTime, dayEndTime, null).stream().collect(Collectors.groupingBy(WindTowerStatusData::getStationCode, Collectors.toList()));
            Map<String, List<WeatherStationStatusData>> weatherStationStatusDataListMap = weatherStationStatusDataService.findByBetweenTimeAndStationCode(dayStartTime, dayEndTime, null).stream().collect(Collectors.groupingBy(WeatherStationStatusData::getStationCode, Collectors.toList()));

            for (ElectricField electricField : electricFieldList) {
                String stationCode = electricField.getStationCode();
                if (powerStationStatusDataListMap.containsKey(stationCode)) {
                    PowerStationStatusData powerStationStatusData = powerStationStatusDataListMap.get(stationCode).stream().max(Comparator.comparing(p -> p.getTime().getTime())).get();
                    setData(Arrays.asList(powerStationStatusData), stationCode);
                }

                if (electricField.getElectricFieldTypeEnum().equals("E2") && windTowerStatusDataListMap.containsKey(stationCode)) {
                    WindTowerStatusData windTowerStatusData = windTowerStatusDataListMap.get(stationCode).stream().max(Comparator.comparing(p -> p.getTime().getTime())).get();
                    setData(Arrays.asList(windTowerStatusData), stationCode);
                }

                if (electricField.getElectricFieldTypeEnum().equals("E1") && weatherStationStatusDataListMap.containsKey(stationCode)) {
                    WeatherStationStatusData weatherStationStatusData = weatherStationStatusDataListMap.get(stationCode).stream().max(Comparator.comparing(p -> p.getTime().getTime())).get();
                    setData(Arrays.asList(weatherStationStatusData), stationCode);
                }

                if (forecastPowerShortTermRegulationListMap.containsKey(stationCode)) {
                    List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulations = forecastPowerShortTermRegulationListMap.get(stationCode);
                    setData(forecastPowerShortTermRegulations, stationCode);
                }

                if (forecastPowerUltraShortTermRegulationListMap.containsKey(stationCode)) {
                    List<ForecastPowerUltraShortTermRegulation> forecastPowerUltraShortTermRegulations = forecastPowerUltraShortTermRegulationListMap.get(stationCode);
                    setData(forecastPowerUltraShortTermRegulations, stationCode);
                }
            }
        }
    }

    @Data
    public class FixedSizeCollection<T> {
        private final int maxSize;
        private final LinkedList<T> list;

        public FixedSizeCollection(int maxSize) {
            this.maxSize = maxSize;
            this.list = new LinkedList<>();
        }

        public void add(T element) {
            list.add(element);
            if (list.size() > maxSize) {
                list.removeFirst();
            }
        }

        public T get(int index) {
            if (index < 0 || index >= list.size()) {
                throw new NoSuchElementException("Index out of bounds: " + index);
            }
            return list.get(index);
        }

        public int size() {
            return list.size();
        }

        public boolean isEmpty() {
            return list.isEmpty();
        }

    }
}
