package com.cpp.web.service.datafactory;

import cn.hutool.core.lang.Filter;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpConfig;
import cn.hutool.extra.ftp.FtpMode;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cpp.web.domain.datafactory.ParsingFileUrl;
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.datafactory.enums.FileTypeEnum;
import com.cpp.web.domain.datafactory.enums.ParsingDataSourcesEnum;
import com.cpp.web.mapper.datafactory.ParsingFileUrlMapper;
import com.cpp.web.mapper.datafactory.ParsingLogMapper;
import com.cpp.web.mapper.datafactory.ParsingTypeMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.util.*;
import java.util.stream.Collectors;

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

    private final ParsingTypeMapper parsingTypeMapper;

    private final Map<String, ParsingInterface> parsingInterfaceMap;

    private final ParsingFileUrlService parsingFileUrlService;

    private final ParsingLogService parsingLogService;

    private final String PARSING_FILE_TEMP_DIR = "/";
    private final String PARSING_FILE_SUCCESS_DIR = "/";
    private final String PARSING_FILE_FAIL_DIR = "/";

    private final File fileTempDir = new File(PARSING_FILE_TEMP_DIR);

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

        List<ParsingType> parsingTypes = parsingTypeMapper.selectList(null);

        if (parsingTypes.size() > 0) {

            List<ParsingFileUrl> fileUrls = parsingFileUrlService.list(Wrappers.lambdaQuery(ParsingFileUrl.class).eq(ParsingFileUrl::getParsingDataSources, ParsingDataSourcesEnum.E1));

            if (fileUrls.size() > 0) {
                Map<FileTypeEnum, List<ParsingFileUrl>> fileTypeEnumListMap = fileUrls.stream().collect(Collectors.groupingBy(ParsingFileUrl::getFileType, Collectors.toList()));

                Ftp ftp = createParsingFtp();
                if (ftp != null) {
                    List<ParsingLog> parsingLogs = new ArrayList<>();
                    for (ParsingType parsingType : parsingTypes) {
                        if (fileTypeEnumListMap.containsKey(parsingType.getFileType())) {
                            ParsingInterface parsingInterface = parsingInterfaceMap.get(parsingType.getFileType().name() + "Parsing");
                            parsingInterface.activationParsingConf();//初始化解析配置
                            List<ParsingFileUrl> fileUrlList = fileTypeEnumListMap.get(parsingType.getFileType());
                            for (ParsingFileUrl parsingFileUrl : fileUrlList) {

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

                                                FileUtils.moveFile(file, failFileDir);
                                            } catch (IOException e) {
                                                log.error("解析文件失败后文件移动失败！场站编号：{}，文件名称：{}", parsingFileUrl.getStationCode(), fileName, e);
                                                e.printStackTrace();
                                            }
                                            file.delete();//失败删除本地文件，等待下次下载
                                            parsingLog.setParsingFileStatus("失败");
                                        } else {
                                            ftp.delFile(ftpUrl + "/" + fileName);//成功删除ftp上的文件

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

                                                FileUtils.moveFile(file, successFileDir);
                                            } catch (IOException e) {
                                                log.error("解析文件成功后文件移动失败！场站编号：{}，文件名称：{}", parsingFileUrl.getStationCode(), fileName, e);
                                                e.printStackTrace();
                                            }
                                            file.delete();//失败删除本地文件，等待下次下载
                                            parsingLog.setParsingFileStatus("成功");
                                        }
                                        parsingLogs.add(parsingLog);
                                    }
                                }
                            }
                        }
                    }

                    try {
                        ftp.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    parsingLogService.saveBatch(parsingLogs);
                }

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

    public Ftp createParsingFtp() {
        Ftp ftp = null;
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream("ftpConfig.properties"));
            String host = prop.getProperty("ftp.server.host");
            String port = prop.getProperty("ftp.server.port");
            String user = prop.getProperty("ftp.server.username");
            String password = prop.getProperty("ftp.server.password");

            FtpConfig ftpConfig = new FtpConfig();
            try {
                //创建ftp配置
                ftpConfig.setHost(host);
                ftpConfig.setPort(Integer.parseInt(port));
                ftpConfig.setPassword(password);
                ftpConfig.setUser(user);
                //字符集使用UTF-8
                ftpConfig.setCharset(Charset.forName("UTF-8"));
                ftpConfig.setConnectionTimeout(5 * 1000L);

                //设置主动模式连接
                ftp = new Ftp(ftpConfig, FtpMode.Active);
                log.info("{} ：ftp服务器连接成功", ftpConfig.getHost());
            } catch (Exception e) {
                log.error("{} ：ftp服务器连接失败", ftpConfig.getHost(), e);
                if (ftp != null) {
                    ftp.close();
                }
            }
        } catch (Exception ex) {
            log.error("获取ftp配置失败。", ex);
        } finally {
            return ftp;
        }
    }


}
