package com.cpp.web.service.cloud;

import com.cpp.common.utils.StringUtils;
import com.cpp.web.domain.cloud.ForecastPowerShortTermCloud;
import com.cpp.web.domain.cloud.NwpCloud;
import com.cpp.web.domain.datafactory.ParsingLog;
import com.cpp.web.domain.datafactory.enums.FileTypeEnum;
import com.cpp.web.domain.enums.AlarmEnum;
import com.cpp.web.domain.enums.DataSourcesEnum;
import com.cpp.web.service.alarm.AbnormalAlarmService;
import com.cpp.web.service.datafactory.ParsingLogService;
import com.cpp.web.utils.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * ftp文件解析
 *
 * @author tl
 * @date 2022-05-11 09:51:21
 */
@AllArgsConstructor
@Slf4j
@Service
public class CloudFileParsing {

    private final ParsingLogService parsingLogService;

    private final AbnormalAlarmService abnormalAlarmService;

    private final ForecastPowerShortTermCloudService forecastPowerShortTermCloudService;
    private final NwpCloudService nwpCloudService;

    private final File fileNewDir = ParsingFileUtil.checkGetPath(ParsingFileUtil.getOutDownload() + File.separator + "new");

    private final ThreadPoolTaskExecutor executor;

    /**
     * 解析文件方法
     * 逻辑：先配置识别文件名称类型：识别文件名称关键字对应的文件类型
     * 有文件类型的配置才可以进入下一步
     * <p>
     * 查找各场站配置的ftp目录，去下载文件并识别文件类型执行对应的解析策略
     * <p>
     * 必要条件：1.配置文件识别类型。2.配置下载路径。3.配置解析公式
     *
     * @param
     * @return
     */
    public void parsingFile() {

        log.info("-----------------开始执行预测文件解析任务----------------------");
        Long currentDate = DateTimeUtil.getMillisecondsSubDay();//今日凌晨
        boolean flag = false;

        //判断该目录是否存在，不存在时创建
        if (!fileNewDir.exists()) {
            fileNewDir.mkdirs();
            log.info("【" + fileNewDir.getPath() + "】目录不存在，系统自动创建文件目录");
        }
        log.info("系统扫描路径【" + fileNewDir.getPath() + "】");
        // 获取指定目录下的文件
        Collection<File> files = FileUtils.listFiles(fileNewDir, new String[]{"RB", "txt"}, false);
        //当前时间格式化为年月日
        String dayStr = new SimpleDateFormat("yyyyMMdd").format(new Date());


        // 如果指定目录下有文件
        if (files.size() > 0) {

            List<ParsingLog> parsingLogs = new ArrayList<>();

            // 循环文件并解析
            for (File file : files) {
                ParsingLog parsingLog = new ParsingLog();
                flag = false;

                String fileName = file.getName();

                parsingLog.setDataSources(DataSourcesEnum.E2);
                parsingLog.setParsingTime(new Date());
                parsingLog.setFileName(fileName);

                // 如果文件不是当然的，删除文件
                if (!fileName.contains(dayStr)) {
                    file.delete();
                    log.warn(fileName + "不是当天的文件，删除！");
                    break;
                }
                // 如果文件名长度符合规则
                if (fileName.length() < 30) {
                    // 如果是短期文件
                    if (file.getName().startsWith("DQ")) {
                        try {
                            parsingLog.setFileType(FileTypeEnum.dq);
                            // 解析短期文件
                            List<ForecastPowerShortTermCloud> listDq = fileAnalysisShortTerm(file, new Date(currentDate));
                            if (!listDq.isEmpty()) {
                                try {
                                    parsingLog.setStationCode(listDq.get(0).getStationCode());
                                    forecastPowerShortTermCloudService.saveBatch(listDq);
                                    flag = true;
                                } catch (Exception e) {
                                    log.error("保存短期数据报错:" + fileName, e);
                                    parsingLog.setParsingDescribe("保存短期数据报错:" + fileName);
                                    flag = false;
                                }
                            } else {
                                log.info(file.getName() + "文件数据内容为空、不能正常解析 、移除该文件:" + fileName);
                                parsingLog.setParsingDescribe("文件数据内容为空、不能正常解析 、移除该文件:" + fileName);
                                flag = false;
                            }


                        } catch (Exception e) {
                            flag = false;
                            parsingLog.setParsingDescribe("解析DQ文件异常:" + fileName);
                            log.error("解析DQ文件失败:" + fileName, e);
                        }
                    }


                    if (file.getName().startsWith("NWP")) {
                        try {
                            parsingLog.setFileType(FileTypeEnum.nwp);
                            List<NwpCloud> listNwp = fileAnalysisNwp(file, new Date(currentDate));
                            if (!listNwp.isEmpty()) {
                                try {
                                    parsingLog.setStationCode(listNwp.get(0).getStationCode());
                                    nwpCloudService.saveBatch(listNwp);
                                    flag = true;
                                } catch (Exception e) {
                                    log.error("保存NWP数据报错:" + fileName, e);
                                    parsingLog.setParsingDescribe("保存NWP数据报错:" + fileName);
                                    flag = false;
                                }
                            } else {
                                parsingLog.setParsingDescribe("文件数据内容为空、不能正常解析 、移除该文件:" + fileName);
                                log.info(file.getName() + "文件数据内容为空、不能正常解析 、移除该文件");
                                flag = false;
                            }
                        } catch (Exception e) {
                            log.error("解析NWP文件失败:" + fileName, e);
                            parsingLog.setParsingDescribe("解析NWP文件失败:" + fileName);
                            flag = false;
                        }
                    }


                    if (flag) {
                        parsingLog.setParsingFileStatus("成功");
                        moveFile(file);
                    } else {
                        parsingLog.setParsingFileStatus("失败");
                        LogUtil.info(DataSourcesEnum.E2, AlarmEnum.E4, parsingLog.getParsingDescribe(), "");
                        //移除文件备份到error文件下
                        moveFileError(file);
                    }
                    parsingLogs.add(parsingLog);
                }
            }
            if (parsingLogs.size() > 0) {
                parsingLogService.saveBatch(parsingLogs);
            }
        }


        log.info("-----------------执行预测文件解析任务完成----------------------");

    }


    /**
     * NWP解析
     *
     * @param file 文件路径
     * @return 样例集合
     */
    private List<NwpCloud> fileAnalysisNwp(File file, Date currentDate) {
        List<NwpCloud> listNwp = new ArrayList<>();
        if (file.renameTo(file)) {
            InputStreamReader readNwp = null;
            BufferedReader bufferedReaderNwp = null;
            try {
                readNwp = new InputStreamReader(Files.newInputStream(file.toPath()), StandardCharsets.UTF_8);//考虑到编码格式
                bufferedReaderNwp = new BufferedReader(readNwp);
                String lineTxt;
                NwpCloud nwpData;
                BigDecimal nwpDirectRadiation = new BigDecimal("0.7"); //直接辐射
                BigDecimal nwpDiffuseRadiation = new BigDecimal("0.3"); //散接辐射


                String stationCode = "";
                String forecastModel = "";


                while ((lineTxt = bufferedReaderNwp.readLine()) != null) {
                    //NWP文件按照Tab方式截取
                    String[] datas = lineTxt.split("\t");

                    if (datas[0].startsWith("<集中")) {
                        stationCode = ParsingUtil.splitLineWithSpace(datas[0])[1].split("'")[1];
                        // 使用单引号分割字符串
                        forecastModel = ParsingUtil.splitLineWithSpace(datas[0])[2].split("'")[1];
                    }


                    if (!stationCode.equals("") && !forecastModel.equals("") && datas.length >= 35 && datas[0].startsWith("#")) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        //过滤当天的数据
                        //if (sdf.parse(datas[5]).getTime() >= TimeUtils.getMillisecondsSubDay() + 24 * 60 * 60 *
                        // 1000) {


                        Date time = sdf.parse(datas[5]);
                        Integer howLongAgo = calcHowLongAgo(currentDate, time);
                        if (howLongAgo > 0) {

                            nwpData = new NwpCloud();

                            //将截取的文件放入nwpData中
                            nwpData.setForecastHowLongAgo(howLongAgo);
                            nwpData.setForecastModel(forecastModel);
                            nwpData.setStationCode(stationCode);
                            nwpData.setTime(time);//采集时间 与 短期预测时间关联
                            nwpData.setT(NumberUtil.subtract(new BigDecimal(datas[6]), new BigDecimal("273.15")));//温度

                            nwpData.setSenf(new BigDecimal(datas[11]).setScale(2, RoundingMode.HALF_UP));//感热
                            nwpData.setSwr(new BigDecimal(datas[9]).setScale(2, RoundingMode.HALF_UP));//短波辐射(相当于总辐射)
                            nwpData.setLwr(new BigDecimal(datas[10]).setScale(2, RoundingMode.HALF_UP));//短波辐射(相当于总辐射)
                            nwpData.setPressure(new BigDecimal(datas[8]).setScale(2, RoundingMode.HALF_UP));//地表气压
                            nwpData.setRh(new BigDecimal(datas[7]).setScale(2, RoundingMode.HALF_UP));//2m相对湿度
                            nwpData.setDiffuseRadiation(new BigDecimal(datas[9]).multiply(nwpDiffuseRadiation).setScale(2, RoundingMode.HALF_UP));//散接辐射
                            nwpData.setDirectRadiation(new BigDecimal(datas[9]).multiply(nwpDirectRadiation).setScale(2, RoundingMode.HALF_UP));//直接辐射

                            nwpData.setWs10(new BigDecimal(datas[19]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs30(new BigDecimal(datas[20]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs50(new BigDecimal(datas[21]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs70(new BigDecimal(datas[22]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs80(new BigDecimal(datas[23]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs90(new BigDecimal(datas[24]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs100(new BigDecimal(datas[25]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWs170(new BigDecimal(datas[26]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd10(new BigDecimal(datas[27]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd30(new BigDecimal(datas[28]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd50(new BigDecimal(datas[29]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd70(new BigDecimal(datas[30]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd80(new BigDecimal(datas[31]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd90(new BigDecimal(datas[32]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd100(new BigDecimal(datas[33]).setScale(2, RoundingMode.HALF_UP));
                            nwpData.setWd170(new BigDecimal(datas[34]).setScale(2, RoundingMode.HALF_UP));
                            if (datas.length > 35) {
                                //nwp 新增解析内容
                                nwpData.setWs20(new BigDecimal(datas[35]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs40(new BigDecimal(datas[36]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs60(new BigDecimal(datas[37]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs110(new BigDecimal(datas[38]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs120(new BigDecimal(datas[39]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs130(new BigDecimal(datas[40]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs140(new BigDecimal(datas[41]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs150(new BigDecimal(datas[42]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs160(new BigDecimal(datas[43]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs180(new BigDecimal(datas[44]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs190(new BigDecimal(datas[45]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWs200(new BigDecimal(datas[46]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd20(new BigDecimal(datas[47]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd40(new BigDecimal(datas[48]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd60(new BigDecimal(datas[49]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd110(new BigDecimal(datas[50]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd120(new BigDecimal(datas[51]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd130(new BigDecimal(datas[52]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd140(new BigDecimal(datas[53]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd150(new BigDecimal(datas[54]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd160(new BigDecimal(datas[55]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd180(new BigDecimal(datas[56]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd190(new BigDecimal(datas[57]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setWd200(new BigDecimal(datas[58]).setScale(2, RoundingMode.HALF_UP));
                                //下面的感觉用不上，先注释
//                            nwpData.setT10(new BigDecimal(datas[59]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT20(new BigDecimal(datas[60]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT30(new BigDecimal(datas[61]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT40(new BigDecimal(datas[62]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT50(new BigDecimal(datas[63]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT60(new BigDecimal(datas[64]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT70(new BigDecimal(datas[65]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT80(new BigDecimal(datas[66]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT90(new BigDecimal(datas[67]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT100(new BigDecimal(datas[68]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT110(new BigDecimal(datas[69]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT120(new BigDecimal(datas[70]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT130(new BigDecimal(datas[71]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT140(new BigDecimal(datas[72]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT150(new BigDecimal(datas[73]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT160(new BigDecimal(datas[74]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT170(new BigDecimal(datas[75]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT180(new BigDecimal(datas[76]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT190(new BigDecimal(datas[77]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setT200(new BigDecimal(datas[78]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setDniCalcd(new BigDecimal(datas[79]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setSolarZenith(new BigDecimal(datas[80]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setClearskyGhi(new BigDecimal(datas[81]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setLcc(new BigDecimal(datas[82]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setMcc(new BigDecimal(datas[83]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setHcc(new BigDecimal(datas[84]).setScale(2, RoundingMode.HALF_UP));
//                            nwpData.setTcc(new BigDecimal(datas[85]).setScale(2, RoundingMode.HALF_UP));
                                nwpData.setTpr(new BigDecimal(datas[86]).setScale(2, RoundingMode.HALF_UP));

                            }
                            listNwp.add(nwpData);
                        }
                    }
                }
            } catch (IOException | ParseException | RuntimeException e) {
                log.error("系统错误：", e);
                File destFile = new File(file.getPath().replaceFirst("new", "error"));
                if (destFile.exists()) {
                    destFile.delete();
                }
                try {
                    FileUtils.moveFile(file, destFile);
                } catch (IOException e1) {
                    log.error(file.getName() + "文件解析失败", e);
                }
            } finally {
                close(bufferedReaderNwp, readNwp);
            }
        }
        return listNwp;
    }

    /**
     * 短期解析
     *
     * @param file        文件路径
     * @param currentDate 当前时间
     * @return 样例集合
     */
    private List<ForecastPowerShortTermCloud> fileAnalysisShortTerm(File file, Date currentDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<ForecastPowerShortTermCloud> forecastPowerShortTerm = new ArrayList<>();
        // 当文件未被使用时，进行解析上报
        if (file.renameTo(file)) {
            InputStreamReader read = null;
            BufferedReader bufferedReader = null;
            String stringLine;
            ForecastPowerShortTermCloud stf;
            try {
                read = new InputStreamReader(Files.newInputStream(file.toPath()), StandardCharsets.UTF_8);
                bufferedReader = new BufferedReader(read);

                String stationCode = "";
                String forecastModel = "";

                // 循环解析文件
                while ((stringLine = bufferedReader.readLine()) != null) {

                    String[] string_arr = stringLine.split("\t");
                    if (string_arr[0].startsWith("<集中")) {
                        stationCode = ParsingUtil.splitLineWithSpace(string_arr[0])[1].split("'")[1];
                        // 使用单引号分割字符串
                        forecastModel = ParsingUtil.splitLineWithSpace(string_arr[0])[2].split("'")[1];
                    }

                    if (string_arr.length == 4) {
                        if (!stationCode.equals("") && !forecastModel.equals("") && string_arr[0].startsWith("#")) {
                            if (StringUtils.isNotEmpty(string_arr[2])) {
                                Date time = sdf.parse(string_arr[2]);
                                Integer howLongAgo = calcHowLongAgo(currentDate, time);
                                if (howLongAgo > 0) {
                                    stf = new ForecastPowerShortTermCloud();
                                    stf.setFpValue(new BigDecimal(string_arr[3]));
                                    stf.setTime(time);
                                    stf.setForecastHowLongAgo(howLongAgo);
                                    stf.setStationCode(stationCode);
                                    stf.setForecastModel(forecastModel);
                                    forecastPowerShortTerm.add(stf);
                                }
                            }
                        }

                    }
                }


            } catch (IOException | ParseException | RuntimeException e) {
                log.error("系统错误：", e);
                File destFile = new File(file.getPath().replaceFirst("new", "error"));
                if (destFile.exists()) {
                    destFile.delete();
                }
                try {
                    FileUtils.moveFile(file, destFile);
                } catch (IOException e1) {
                    log.error(file.getName() + "文件解析失败", e);
                }
            } finally {
                close(bufferedReader, read);
            }
        }
        return forecastPowerShortTerm;
    }


    /**
     * 关闭文件流
     *
     * @param bufferedReader 字符数据
     * @param read           字节流
     */
    private void close(BufferedReader bufferedReader, InputStreamReader read) {
        try {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
            if (read != null) {
                read.close();
            }
        } catch (IOException e) {
            log.error("关闭文件流失败:", e);
        }
    }


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


    /**
     * 移动文件到临时目录下
     *
     * @param file 文件
     */
    private void moveFile(File file) {
        // 移动文件到处理目录
        File destFile = new File(file.getPath().replaceFirst("new", "backupsTemp"));
        log.info("move file :{}, dest file:{}", file, destFile);
        if (destFile.exists()) {
            destFile.delete();
        }
        try {
            FileUtils.moveFile(file, destFile);
        } catch (IOException e) {
            log.error("系统移除文件错误：", e);
        }
        moveFileBackups(destFile.getParent());
    }


    /**
     * 移动文件到处理目录
     *
     * @param filePath 文件路径
     */
    private void moveFileBackups(String filePath) {
        String targetRoot = filePath.replaceFirst("backupsTemp", "backups");
        String path = mkDirForTime(targetRoot, null);
        path = mkDirForTime(path, "yyyy");
        path = mkDirForTime(path, "MM");
        path = mkDirForTime(path, "yyyyMMdd");
        path = mkDirForTime(path, "HHmm");
        // 移动文件夹内容
        File sourceFile = new File(filePath);
        if (sourceFile.exists()) {
            try {
                File[] files = sourceFile.listFiles();
                if (files != null) {
                    for (File f : files) {
                        if (f.renameTo(new File(path + f.getName()))) {
                            log.info("move file :{}, dest file:{}", path, f.getName());
                        }
                    }
                }
            } catch (Exception e) {
                log.error("文件移动失败" + e);
            }
        }
    }

    /**
     * 创建备份文件夹
     */
    public static String mkDirForTime(String targetRoot, String format) {
        String path;
        File file;
        if (StringUtils.isNotEmpty(format)) {
            long current = System.currentTimeMillis();
            path = DateFormatUtils.format(current, format);
            file = new File(targetRoot + File.separator + path + File.separator);

        } else {
            file = new File(targetRoot + File.separator);
        }

        if (!file.exists() && !file.isFile()) {

            if (file.mkdir()) {
                log.info("已创建文件夹");
            } else {
                log.info("创建文件夹失败,路径：" + file.getPath());
            }
        }
        return file.getPath() + File.separator;
    }


    /**
     * 移动文件到处理错误目录下
     *
     * @param file 文件
     */
    private void moveFileError(File file) {
        File destFile = new File(file.getPath().replaceFirst("new", "error"));
        if (destFile.exists()) {
            destFile.delete();
        }
        try {
            FileUtils.moveFile(file, destFile);
        } catch (IOException e) {
            log.error(file.getName() + "文件解析失败", e);
        }
    }
}
