package com.nokia.sms.task;

import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import com.nokia.common.ssh.SSHUtil;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import de.siegmar.fastcsv.reader.CsvReader;
import de.siegmar.fastcsv.reader.CsvRow;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Component
@Slf4j
public class SyncTask {
    /**
     * 无文件次数
     */
    public static final AtomicInteger NO_FILE_COUNT = new AtomicInteger(0);
    /**
     * 记录上次时间的文件路径
     */
    public static final String LAST_FILE = "/data1/sms_blk/sms_blk_interface/last.txt";
    /**
     * 无文件次数告警值
     */
    public static final int ALERT_COUNT = 15;
    /**
     * 两个文件时间间隔告警阈值
     */
    public static final int INTERVAL = 6;
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    @Value("${sms.blk.ssh.host:10.17.182.24}")
    private String host;
    @Value("${sms.blk.ssh.port:22}")
    private Integer port;
    @Value("${sms.blk.ssh.username:sassftp}")
    private String username;
    @Value("${sms.blk.ssh.password:Flzx3qc!}")
    private String password;
    @Value("${sms.blk.ssh.sourceDir:files/black_list/}")
    private String sourceDir;
    @Value("${sms.blk.ssh.targetDir:download/}")
    private String downloadTargetDir;
    private SSHUtil sshUtil;

    private final JdbcTemplate jdbcTemplate;

    public SyncTask(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @XxlJob("smsBlackListSync")
    public void smsBlackListSync() {
        try {
            // 创建文件夹
            Files.createDirectories(Paths.get(downloadTargetDir));
            sshUtil = new SSHUtil(host, port, username, password);
            // 获取文件列表
            List<String> list = sshUtil.ls(sourceDir);
            if (CollectionUtils.isEmpty(list)) {
                int count = NO_FILE_COUNT.incrementAndGet();
                log.info("{}次没有文件", count);
                XxlJobHelper.log("{}次没有文件", count);
                // 无文件次数告警
                if (count > ALERT_COUNT) {
                    log.error("超过{}分钟没有文件", ALERT_COUNT);
                    XxlJobHelper.log("超过{}分钟没有文件", ALERT_COUNT);
                    // 重置无文件计数
                    NO_FILE_COUNT.set(0);
                }
                return;
            }

            // 重置无文件计数
            NO_FILE_COUNT.set(0);
            log.info("扫描到的文件: {}", list);
            XxlJobHelper.log("扫描到的文件: {}", list);
            // 过滤文件
            List<String> newList = list.stream().filter(t -> t.startsWith("Blacklist_SASS_") && !t.endsWith(".tmp"))
                    .collect(Collectors.toList());
            // 检查文件
            String lastString = readLast();
            log.info("last: {}", lastString);
            LocalDateTime last = StringUtils.hasText(lastString)
                    ? getLocalDateTime(lastString)
                    : getLocalDateTime(getTimeString(newList.get(0)));
            int size = list.size();
            checkFile(last, list, size, 0);
            // 入库
            for (String t : newList) {
                // 删除空文件
                if (t.endsWith("_0")) {
                    log.info("删除空文件: {}", t);
                    XxlJobHelper.log("删除空文件: {}", t);
                    sshUtil.rm(sourceDir + t);
                    updateLast(t);
                    continue;
                }
                singleTask(t);
            }
        } catch (Exception e) {
            log.error("发生异常了: {}", e.getMessage(), e);
            XxlJobHelper.log("发生异常了: {}", e.getMessage(), e);
            XxlJobHelper.handleFail(e.getMessage());
        } finally {
            try {
                sshUtil.disconnect();
            } catch (IOException e) {
                log.error("发生异常了: {}", e.getMessage(), e);
                XxlJobHelper.log("发生异常了: {}", e.getMessage(), e);
                XxlJobHelper.handleFail(e.getMessage());
            }
        }
    }

    /**
     * 更新上次时间
     */
    public static void updateLast(String filename) {
        writeFile(LAST_FILE, getTimeString(filename));
    }

    /**
     * 读取上次时间
     */
    public static String readLast() {
        return readFile(LAST_FILE);
    }

    /**
     * 写文件
     * @param filename 文件名
     * @param content 内容
     */
    public static void writeFile(String filename, String content) {
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filename))) {
            bufferedWriter.write(content);
        } catch (IOException e) {
            log.error("写{}失败", filename);
            XxlJobHelper.log("写{}失败", filename);
        }
    }

    /**
     * 读文件内容
     * @param filename 文件名
     */
    public static String readFile(String filename) {
        List<String> list = null;
        try {
            list = Files.readAllLines(Paths.get(filename), StandardCharsets.UTF_8);
        } catch (IOException e) {
            log.error("读{}失败", filename);
            XxlJobHelper.log("读{}失败", filename);
        }
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }

        return list.get(0);
    }

    /**
     * 检查文件
     * @param t1 上次时间
     * @param list 文件名列表
     * @param size 文件名列表大小
     * @param index 索引
     */
    public static void checkFile(LocalDateTime t1, List<String> list, int size, int index) {
        if (index >= size) {
            return;
        }

        LocalDateTime t2 = getLocalDateTime(getTimeString(list.get(index)));
        long diff = Duration.between(t1, t2).toMinutes();
        if (diff > INTERVAL) {
            log.error("{} ~ {} 之间缺少文件", getTimeString(t1), getTimeString(t2));
        }

        checkFile(t2, list, size, index + 1);
    }

    /**
     * 文件名截取时间字符串
     */
    public static String getTimeString(String filename) {
        return filename.split("_")[2];
    }

    /**
     * 时间字符串转LocalDateTime
     */
    public static LocalDateTime getLocalDateTime(String time) {
        return LocalDateTime.parse(time, DATE_TIME_FORMATTER);
    }

    /**
     * 格式化LocalDateTime
     */
    public static String getTimeString(LocalDateTime time) {
        return time.format(DATE_TIME_FORMATTER);
    }

    /**
     * 单一任务
     *
     * @param filename 文件名
     */
    public void singleTask(String filename) throws JSchException, IOException, SftpException {
        download(filename);
        importData(filename);
    }

    /**
     * 下载文件
     *
     * @param filename 文件名
     */
    public void download(String filename) throws JSchException, IOException, SftpException {
        log.info("下载文件: {}", filename);
        XxlJobHelper.log("下载文件: {}", filename);
        String src = sourceDir + filename;
        String dst = downloadTargetDir + filename;
        sshUtil.get(src, dst);
        // 删除远程文件
        log.info("删除远程文件: {}", filename);
        XxlJobHelper.log("删除远程文件: {}", filename);
        sshUtil.rm(src);
        updateLast(filename);
    }

    /**
     * 导入数据
     *
     * @param filename 文件名
     * @throws IOException ioexception
     */
    public void importData(String filename) throws IOException {
        List<Object[]> l1 = new ArrayList<>();
        List<Object[]> l2 = new ArrayList<>();
        log.info("导入: {}", filename);
        XxlJobHelper.log("导入: {}", filename);
        Path path = Paths.get(downloadTargetDir + filename);
        try (CsvReader csvReader = CsvReader.builder().build(path, StandardCharsets.UTF_8)) {
            for (CsvRow row : csvReader) {
                Object[] o1 = new Object[]{
                        row.getField(0),
                        Integer.valueOf(row.getField(1)),
                        row.getField(2),
                        row.getField(3),
                        row.getField(4),
                        Integer.valueOf(row.getField(5)),
                        Integer.valueOf(row.getField(6)),
                };
                l1.add(o1);
                Object[] o2 = new Object[]{
                        row.getField(0),
                        Integer.valueOf(row.getField(1)),
                        row.getField(2),
                        row.getField(3),
                        row.getField(4),
                        Integer.valueOf(row.getField(5)),
                        Integer.valueOf(row.getField(6)),
                        filename
                };
                l2.add(o2);
            }
        }
        String sql = "insert into sms_blk.blacklist " +
                "(phone, reason, start_time, end_time, hit_desc, operation_type, suggestion) " +
                "values (?, ?, ?, ?, ?, ?, ?) " +
                "on conflict (phone) " +
                "do update set (reason, start_time, end_time, hit_desc, operation_type, suggestion) = " +
                "(excluded.reason, excluded.start_time, excluded.end_time, excluded.hit_desc, " +
                "excluded.operation_type, excluded.suggestion)";
        jdbcTemplate.batchUpdate(sql, l1);
        sql = "insert into sms_blk.blacklist_file " +
                "(phone, reason, start_time, end_time, hit_desc, operation_type, suggestion, filename) " +
                "values (?, ?, ?, ?, ?, ?, ?, ?)";
        jdbcTemplate.batchUpdate(sql, l2);
        // todo: 删除本地文件
//        Files.deleteIfExists(path);
//        log.info("删除本地文件: {}", filename);
    }
}
