package com.cpp.web.service.datafactory.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cpp.common.utils.spring.SpringUtils;
import com.cpp.system.service.ISysConfigService;
import com.cpp.web.domain.datafactory.BaseParsing;
import com.cpp.web.domain.datafactory.ParsingDq;
import com.cpp.web.domain.datafactory.dto.ParsingConfParam;
import com.cpp.web.domain.datafactory.dto.ParsingResultDto;
import com.cpp.web.domain.datafactory.enums.FileTypeEnum;
import com.cpp.web.domain.enums.UploadStatusEnum;
import com.cpp.web.domain.regulation.TempShortRegulation;
import com.cpp.web.domain.station.ForecastPowerShortTermRegulation;
import com.cpp.web.domain.station.ForecastPowerShortTermSend;
import com.cpp.web.domain.station.ForecastPowerShortTermStation;
import com.cpp.web.service.datafactory.DataStore;
import com.cpp.web.service.datafactory.ParsingDqService;
import com.cpp.web.service.datafactory.ParsingInterface;
import com.cpp.web.service.datafactory.ScheduledHelper;
import com.cpp.web.service.regulation.TempShortRegulationService;
import com.cpp.web.service.station.ForecastPowerShortTermRegulationService;
import com.cpp.web.service.station.ForecastPowerShortTermSendService;
import com.cpp.web.utils.DateTimeUtil;
import com.cpp.web.utils.ParsingFieldUtil;
import com.cpp.web.utils.ParsingFileUtil;
import com.cpp.web.utils.ParsingUtil;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 调控后短期解析业务层实现类 开头r代表调控后
 *
 * @author tl
 * @date 2022-05-11 18:07:03
 */
@Service("rdqParsing")
@Slf4j
@RequiredArgsConstructor
@Transactional
public class ParsingRdqServiceImpl implements ParsingInterface {
    private final ISysConfigService configService;

    private final ForecastPowerShortTermRegulationService forecastPowerShortTermRegulationService;

    private final ParsingDqService parsingDqService;

    private List<ParsingDq> parsingDqs = new ArrayList<>();

    private final TempShortRegulationService tempShortRegulationService;

    private final ForecastPowerShortTermSendService forecastPowerShortTermSendService;

    @Override
    public boolean save(BaseParsing parsing) {
        return true;
    }

    @Override
    public boolean deleteById(Long id) {
        return true;
    }

    @Override
    public boolean activationParsingConf() {
        this.parsingDqs = parsingDqService.list();
        return true;
    }

    @Override
    public ParsingResultDto parsing(File file, String stationCode) {
        ParsingResultDto parsingResultDto = new ParsingResultDto();
        parsingResultDto.setFileType(FileTypeEnum.dq.name());
        parsingResultDto.setFileName(file.getName());
        try {
            List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulationList = new ArrayList();
            if (!this.parsingDqs.isEmpty()) {
                ParsingDq parsingDqInfo = this.parsingDqs.get(0);
                List<String> fileContent = ParsingFileUtil.getFileContent(file);
                if (parsingDqInfo.getDataType().equals(ParsingFieldUtil.MULTI)) {//多行操作
                    Date forecastTime = parsingForecastTime(parsingDqInfo.getForecastTime(), fileContent);
                    Date genDate = DateTimeUtil.getDayStartTime(forecastTime.getTime() - 86400000L);
                    String status = parsingUploadStatus("2<=>5<=>status=", fileContent);
                    Date time = forecastTime;
                    if (forecastTime != null) {//解析时间成功才可以进行以下操作
                        if (parsingDqInfo.getFpValue() != null) {
                            List<BigDecimal> fpValues = parsingFpValue(parsingDqInfo.getFpValue(), fileContent);
                            if (fpValues.size() > 0) {
                                BigDecimal capacity = parsingValue(parsingDqInfo.getCapacity(), fileContent);
                                for (BigDecimal fpValue : fpValues) {
                                    ForecastPowerShortTermRegulation forecastPowerShortTermRegulation = new ForecastPowerShortTermRegulation();
                                    forecastPowerShortTermRegulation.setStationCode(stationCode);
                                    forecastPowerShortTermRegulation.setFpValue(fpValue);
                                    forecastPowerShortTermRegulation.setGenDate(genDate);
                                    forecastPowerShortTermRegulation.setTime(time);
                                    forecastPowerShortTermRegulation.setForecastHowLongAgo(calcHowLongAgo(forecastTime, time));
                                    forecastPowerShortTermRegulation.setCapacity(capacity);
                                    forecastPowerShortTermRegulationList.add(forecastPowerShortTermRegulation);
                                    time = new Date(time.getTime() + 900000L);//递增15分钟
                                }
                                int dqDay = 10;
                                try {
                                    dqDay = Integer.parseInt(configService.selectConfigByKey("require_dq_day"));
                                } catch (Exception e) {
                                }
                                if (forecastPowerShortTermRegulationList.size() == dqDay * 96) {
                                    List<BigDecimal> openCapacityList = parsingOpenCapacityValue(parsingDqInfo.getOpenCapacity(), fileContent);
                                    if (openCapacityList != null && openCapacityList.size() == fpValues.size()) {
                                        for (int i = 0; i < forecastPowerShortTermRegulationList.size(); i++) {
                                            forecastPowerShortTermRegulationList.get(i).setOpenCapacity(openCapacityList.get(i));
                                        }
                                    }
                                    scheduleAddCache(forecastPowerShortTermRegulationList, stationCode);
                                    forecastPowerShortTermRegulationService.remove(
                                            new QueryWrapper<ForecastPowerShortTermRegulation>()
                                                    .eq("gen_date", genDate)
                                                    .eq("station_code", stationCode)
                                                    .between("time", forecastPowerShortTermRegulationList.get(0).getTime(), forecastPowerShortTermRegulationList.get(forecastPowerShortTermRegulationList.size() - 1).getTime()));

                                    forecastPowerShortTermRegulationService.saveBatch(forecastPowerShortTermRegulationList);
                                    log.info("[" + stationCode + "]解析调控后短期文件：{} 成功！ O(∩_∩)O", file.getName());
                                    parsingResultDto.setStatus("success");
                                    //使用新线程写入状态，不会影响解析速度
                                    SpringUtils.getBean(ThreadPoolTaskExecutor.class).execute(new Runnable() {
                                        @Override
                                        public void run() {
                                            verifyRegulation(stationCode, forecastPowerShortTermRegulationList, forecastTime, status);
                                        }
                                    });
                                } else {
                                    parsingResultDto.setMessage("解析调控后短期文件数据量与要求不一致");
                                    log.error("[" + stationCode + "]解析调控后短期文件数据量与要求不一致，fileName：{}", file.getName());
                                }
                            } else {
                                parsingResultDto.setMessage("解析调控后短期文件时间错误");
                                log.error("[" + stationCode + "]解析调控后短期文件时间错误，fileName：{}", file.getName());
                            }
                        } else {
                            parsingResultDto.setMessage("调控后短期文件里日期不对");
                            log.error("[" + stationCode + "]调控后短期文件里日期不对，fileName：{}", file.getName());
                        }
                    } else {
                        parsingResultDto.setMessage("解析 调控后短期文件无数据");
                        log.error("[" + stationCode + "]解析调控后短期文件无数据，fileName：{}", file.getName());
                    }
                } else {
                    parsingResultDto.setMessage("调控后短期暂无单行解析方式");
                    log.info("[" + stationCode + "]调控后短期暂无单行解析方式，fileName：{}", file.getName());
                }
            }
        } catch (Exception e) {
            parsingResultDto.setMessage("解析调控后短期数据失败!");
            log.error("[" + stationCode + "]解析调控后短期数据失败！ /(ㄒoㄒ)/~~，fileName：{}", file.getName(), e);
        } finally {
            return parsingResultDto;
        }
    }

    /**
     * 校验调控后数据是否成功上传
     *
     * @param stationCode
     * @param forecastPowerShortTermRegulationList
     * @param forecastTime
     * @param status
     */
    private void verifyRegulation(String stationCode, List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulationList, Date forecastTime, String status) {
        String dqEndTime = configService.selectConfigByKey("dqEndTime");
        if (dqEndTime.equals(StringUtils.EMPTY)) {
            dqEndTime = "08:00";
        }
        try {
            Long longTime = forecastTime.getTime() - 86400000L;
            Date startTime = DateTimeUtil.getDayStartTime(longTime);
            Date endTime = DateUtils.parseDate(DateFormatUtils.format(startTime, "yyyy-MM-dd " + dqEndTime), "yyyy-MM-dd HH:mm");
            QueryWrapper<TempShortRegulation> tk_date = new QueryWrapper<TempShortRegulation>().between("tk_date", startTime, endTime).eq("station_code", stationCode);
            List<TempShortRegulation> tempShortRegulations = tempShortRegulationService.list(tk_date);
            if (tempShortRegulations.size() > 0) {
                if (status.equals("success")) {
                    List<ForecastPowerShortTermSend> forecastPowerShortTermSendList = forecastPowerShortTermSendService.findByForecastTimeBetweenAndForecastHowLongAgoAndStationCode(forecastPowerShortTermRegulationList.get(0).getTime().getTime(), forecastPowerShortTermRegulationList.get(forecastPowerShortTermRegulationList.size() - 1).getTime().getTime(), -99, stationCode);
                    Map<Date, List<ForecastPowerShortTermSend>> dateListMap = forecastPowerShortTermSendList.stream().collect(Collectors.groupingBy(ForecastPowerShortTermSend::getTime, Collectors.toList()));
                    Map<Date, List<ForecastPowerShortTermRegulation>> listMap = forecastPowerShortTermRegulationList.stream().collect(Collectors.groupingBy(ForecastPowerShortTermRegulation::getTime, Collectors.toList()));
                    String uploadStatus = UploadStatusEnum.E1.name();
                    for (Map.Entry<Date, List<ForecastPowerShortTermRegulation>> dateListEntry : listMap.entrySet()) {
                        if (dateListMap.containsKey(dateListEntry.getKey())) {
                            if (dateListMap.get(dateListEntry.getKey()).get(0).getFpValue().compareTo(dateListEntry.getValue().get(0).getFpValue()) != 0) {
                                uploadStatus = UploadStatusEnum.E2.name();
                                log.info("回传数据与下发数据不符，调控失败{}", stationCode);
                                break;
                            }
                        } else {
                            uploadStatus = UploadStatusEnum.E2.name();
                            log.info("回传数据时间与下发数据时间不符，调控失败{}", stationCode);
                            break;
                        }
                    }
                    for (TempShortRegulation tempShortRegulation : tempShortRegulations) {
                        tempShortRegulation.setUploadStatusEnum(uploadStatus);
                    }
                } else {
                    log.info("站端返回上报结果失败，调控失败{}", stationCode);
                    for (TempShortRegulation tempShortRegulation : tempShortRegulations) {
                        tempShortRegulation.setUploadStatusEnum(UploadStatusEnum.E2.name());
                    }
                }
                tempShortRegulationService.saveOrUpdateBatch(tempShortRegulations);
            }
        } catch (Exception e) {
            log.error("验证调控失败", e);
            e.printStackTrace();
        }
    }

    protected Date parsingForecastTime(String sign, List<String> fileContent) {
        Date forecastTime = null;
        try {
            ParsingConfParam config = ParsingFieldUtil.getConfig(sign);
            String s = fileContent.get(ParsingUtil.noParseInt(config.getLineNumber()));
            int signIndex = s.indexOf(config.getDataSign());
            String result;
            if (signIndex > 0) {
                int startLength = config.getDataSign().length() + signIndex + 1;
                int length = config.getFormat().length();
                if (String.valueOf(s.charAt(startLength + 1)).equals("'")) {
                    startLength += 1;
                }
                result = s.substring(startLength, startLength + length);

                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(config.getFormat());

                forecastTime = simpleDateFormat.parse(result);

                log.info("解析 调控后短期【预测时间】成功==》开始时间为：{}", result);
            } else {
                log.error("解析 调控后短期【预测时间】时公式标识不存在所在行");
            }
        } catch (RuntimeException e) {
            log.error("解析 调控后短期【预测时间】失败");
            e.printStackTrace();
        } finally {
            return forecastTime;
        }

    }


    /*
     * 解析配置格式规则：
     *   s=>>1=>>entity=
     *   s：单行标识
     *   1：取值在1行
     *   entity=：键标记标记
     *
     *   m=>>4-20=>>3
     *   m：多行标识
     *   4-20：取值在4-20行
     *   3：第三列
     * */
    private List<BigDecimal> parsingFpValue(String sign, List<String> fileContent) {
        List<BigDecimal> fpValues = new ArrayList<>();
        try {
            parsingValues(sign, fileContent, fpValues);

            log.info("解析 调控后短期【预测数据】成功！！！==》记录数：{}", fpValues.size());
        } catch (RuntimeException e) {
            log.error("解析 调控后短期【预测数据】失败");
            e.printStackTrace();
        } finally {
            return fpValues;
        }
    }

    private void parsingValues(String sign, List<String> fileContent, List<BigDecimal> fpValues) {
        ParsingConfParam config = ParsingFieldUtil.getConfig(sign);


        String[] lineNumber = config.getLineNumber().split("-");
        int startLine = ParsingUtil.noParseInt(lineNumber[0]);
        int endLine = ParsingUtil.noParseInt(lineNumber[1]);

        for (int i = startLine; i <= endLine; i++) {
            String[] strings = ParsingUtil.splitLineWithSpace(fileContent.get(i));
            String result = strings[ParsingUtil.noParseInt(config.getRowNumber())];

            fpValues.add(new BigDecimal(result));
        }
    }


    private List<BigDecimal> parsingOpenCapacityValue(String sign, List<String> fileContent) {
        List<BigDecimal> openCapacityValues = new ArrayList<>();
        try {
            if (sign.equals("") || sign == null) {
                openCapacityValues = null;
            } else {
                parsingValues(sign, fileContent, openCapacityValues);
                log.info("解析 调控后短期【开机容量】成功！！！==》记录数：{}", openCapacityValues.size());
            }
        } catch (RuntimeException e) {
            log.error("解析 调控后短期【开机容量】失败");
            openCapacityValues = null;
            e.printStackTrace();
        } finally {
            return openCapacityValues;
        }
    }


    /**
     * 计算D+x中的x
     *
     * @param baseTime
     * @param time
     */
    public Integer calcHowLongAgo(Date baseTime, Date time) {
        return ((int) ((time.getTime() - baseTime.getTime()) / 86400000L)) + 1;
    }


//    private final ScheduledHelper scheduledHelper = new ScheduledHelper();

    public void scheduleAddCache
            (List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulationList, String stationCode) {
        if (forecastPowerShortTermRegulationList.get(0).getGenDate().equals(DateTimeUtil.getDayStartTime(System.currentTimeMillis()))) {
            int ago = 1;
            try {
                String dqHowLongAgo = configService.selectConfigByKey("dqHowLongAgo");
                ago = Integer.parseInt(dqHowLongAgo);
            } catch (Exception e) {
            }
            Long dAdd = System.currentTimeMillis() + ago * 86400000L;
            Date startTime = DateTimeUtil.getDayStartTime(dAdd);
            Date endTime = DateTimeUtil.getDayLastTime(dAdd);
            List<ForecastPowerShortTermRegulation> forecastPowerShortTermRegulations = forecastPowerShortTermRegulationList.stream().filter(f -> f.getTime().getTime() >= startTime.getTime() && f.getTime().getTime() < endTime.getTime()).collect(Collectors.toList());
            if (forecastPowerShortTermRegulations.size() >= 96) {
                DataStore.getInstance().setData(forecastPowerShortTermRegulations, stationCode);
            }
        }
    }
}
