package com.cpp.web.service.datafactory;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import com.cpp.web.domain.datafactory.SftpChannel;
import com.cpp.web.domain.datafactory.ParsingLog;
import com.cpp.web.domain.datafactory.ParsingType;
import com.cpp.web.domain.datafactory.dto.ParsingResultDto;
import com.cpp.web.domain.enums.AlarmEnum;
import com.cpp.web.domain.enums.DataSourcesEnum;
import com.cpp.web.domain.station.ElectricField;
import com.cpp.web.service.station.ElectricFieldService;
import com.cpp.web.utils.LogUtil;
import com.cpp.web.utils.ParsingFileUtil;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
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.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

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

    private final ParsingTypeService parsingTypeService;

    private final Map<String, ParsingInterface> parsingInterfaceMap;

    private final SftpChannelService sftpChannelService;

    private final ElectricFieldService electricFieldService;

    private final ParsingLogService parsingLogService;

    private final File PARSING_FILE_TEMP_DIR = ParsingFileUtil.checkGetPath(ParsingFileUtil.getSftpDownload() + File.separator + "temp");
    private final File PARSING_FILE_SUCCESS_DIR = ParsingFileUtil.checkGetPath(ParsingFileUtil.getSftpDownload() + File.separator + "success");
    private final File PARSING_FILE_FAIL_DIR = ParsingFileUtil.checkGetPath(ParsingFileUtil.getSftpDownload() + File.separator + "fail");

//    private AlarmLog alarmLog = AlarmLog.getInstance();
//    private final ThreadPoolTaskExecutor executor;

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


        log.info("-----------------开始执行FTP文件解析任务----------------------");

        List<ParsingType> parsingTypes = parsingTypeService.list();

        if (parsingTypes.size() > 0) {

            List<SftpChannel> channels = sftpChannelService.list();

            List<ElectricField> electricFields = electricFieldService.list();
            for (SftpChannel channel : channels) {

                List<ElectricField> channelElectricFields = electricFields.stream().filter(e -> e.getSftpChanelId() != null && e.getSftpChanelId().equals(channel.getId())).collect(Collectors.toList());

                if (channelElectricFields.size() > 0) {
                    executeSftpParsing(channel, parsingTypes, electricFields);
                }
            }


        } else {
            log.info("未配置文件识别类型标识，无法进行下载解析");
        }

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

    /**
     * sftp下载文件并解析文件的具体步骤
     *
     * @param parsingTypes
     * @param channelElectricFields
     * @param sftp
     */
    private void parsingFile(List<ParsingType> parsingTypes, List<ElectricField> channelElectricFields, Sftp sftp) {
        List<ParsingLog> parsingLogs = new ArrayList<>();
        for (ParsingType parsingType : parsingTypes) {
//            executor.execute(new Runnable() {
//                @Override
//                public void run() {
            ParsingInterface parsingInterface = parsingInterfaceMap.get(parsingType.getFileType().name() + "Parsing");
            parsingInterface.activationParsingConf();//初始化解析配置
            for (ElectricField electricField : channelElectricFields) {

                String ftpUrl = electricField.getSftpUrl();
                List<String> fileNames = sftp.ls(ftpUrl).stream().filter(f -> f.contains(parsingType.getFileName())).collect(Collectors.toList());
                if (fileNames.size() > 0) {
                    for (String fileName : fileNames) {
                        try {
                            String sftpFileNameDir = ftpUrl + "/" + fileName;
                            String destFileNameDir = PARSING_FILE_TEMP_DIR.getPath() + File.separator + fileName;
                            //下载文件到临时目录
                            sftp.get(sftpFileNameDir, destFileNameDir);
                            File file = FileUtils.getFile(PARSING_FILE_TEMP_DIR, fileName);
                            ParsingLog parsingLog = new ParsingLog();
                            Date now = new Date();
                            parsingLog.setParsingTime(now);
                            parsingLog.setFileName(fileName);
                            ParsingResultDto parsingResultDto = parsingInterface.parsing(file, electricField.getStationCode());
                            parsingLog.setParsingDescribe(parsingResultDto.getMessage());
                            parsingLog.setFileType(parsingType.getFileType());
                            parsingLog.setDataSources(DataSourcesEnum.E1);
                            parsingLog.setStationCode(electricField.getStationCode());
                            if (parsingResultDto.getStatus().equals("fail")) {
                                try {
                                    File failFileDir = new File(PARSING_FILE_FAIL_DIR.getPath() + File.separator + DateFormatUtils.format(now, "yyyy-MM-dd"));
                                    File failFile = new File(failFileDir.getPath() + File.separator + fileName);
                                    if (failFile.exists()) {
                                        failFile.delete();
                                        log.error("已有过解析失败文件，错误文件将覆盖！场站编号：{}，文件名称：{}", electricField.getStationCode(), fileName);
                                    }

                                    ParsingFileUtil.move(file.getAbsolutePath(), failFileDir.getAbsolutePath(), true);
                                } catch (IOException e) {
                                    log.error("解析文件失败后文件移动失败！场站编号：{}，文件名称：{}", electricField.getStationCode(), fileName, e);
                                    e.printStackTrace();
                                }
                                file.delete();//失败删除本地文件，等待下次下载
                                parsingLog.setParsingFileStatus("失败");


                                LogUtil.info(DataSourcesEnum.E3, AlarmEnum.E4, LogUtil.format("无法解析场站端文件：{}", fileName), electricField.getStationCode());

                            } else {
                                sftp.delFile(ftpUrl + "/" + fileName);//成功删除ftp上的文件

                                try {
                                    File successFileDir = new File(PARSING_FILE_SUCCESS_DIR.getPath() + File.separator + DateFormatUtils.format(now, "yyyy-MM-dd"));
                                    File successFile = new File(successFileDir.getPath() + File.separator + fileName);
                                    if (successFile.exists()) {
                                        successFile.delete();
                                        log.error("已有过解析成功文件，成功文件将覆盖！场站编号：{}，文件名称：{}", electricField.getStationCode(), fileName);
                                    }

                                    ParsingFileUtil.move(file.getAbsolutePath(), successFileDir.getAbsolutePath(), true);
                                } catch (IOException e) {
                                    log.error("解析文件成功后文件移动失败！场站编号：{}，文件名称：{}", electricField.getStationCode(), fileName, e);
                                    e.printStackTrace();
                                }
                                file.delete();//失败删除本地文件，等待下次下载
                                parsingLog.setParsingFileStatus("成功");
                            }
                            parsingLogs.add(parsingLog);
                        } catch (Exception e) {
                            LogUtil.info(DataSourcesEnum.E3, AlarmEnum.E4, LogUtil.format("无法解析场站端文件：{}", fileName), electricField.getStationCode());
                            log.error("下载并解析文件{}时异常", fileName, e);
                        }

                    }
                }
            }
//                }
//            });

        }

        parsingLogService.saveBatch(parsingLogs);
    }


    /**
     * 创建sftp 并解析
     *
     * @param sftpChannel
     * @param parsingTypes
     * @param channelElectricFields
     */
    public void executeSftpParsing(SftpChannel sftpChannel, List<ParsingType> parsingTypes, List<ElectricField> channelElectricFields) {
        // SFTP方式上报
        Sftp sftp = null;
        JSch jsch = new JSch();
        ChannelSftp channel = null;
        Session sshSession = null;
        try {
            try {
                sshSession = jsch.getSession(sftpChannel.getUsername(), sftpChannel.getIp(), sftpChannel.getPort());
                sshSession.setPassword(sftpChannel.getPassword());
                sshSession.setTimeout(30000);
                Properties sshConfig = new Properties();
                sshConfig.put("StrictHostKeyChecking", "no");
                sshSession.setConfig(sshConfig);
                log.info("sftp开始连接...");
                sshSession.connect();
                channel = (ChannelSftp) sshSession.openChannel("sftp");
                channel.connect();
                Class cl = ChannelSftp.class;
                Field f = cl.getDeclaredField("server_version");
                f.setAccessible(true);
                f.set(channel, 2);
                channel.setFilenameEncoding("UTF-8");
                sftp = new Sftp(channel, CharsetUtil.CHARSET_UTF_8);

                parsingFile(parsingTypes, channelElectricFields, sftp);
            } catch (Exception e) {
                log.error("sftp连接异常：" + e);
            }

        } finally {
            if (sftp != null) {
                log.info("sftp客户端关闭");
                JschUtil.closeAll();
                sftp.close();
                channel.quit();
                channel.disconnect();
                sshSession.disconnect();
            }
        }
    }

}
