package org.wfc.common.license.runner;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.wfc.common.license.LicenseVerify;
import org.wfc.common.license.domain.LicenseCheckModel;
import org.wfc.common.license.domain.LicenseConstants;
import org.wfc.common.license.domain.LicenseVerifyParam;
import org.wfc.common.license.serverinfo.AbstractServerInfos;
import org.wfc.common.license.utils.LicenseUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@Data
public class LicenseCheckRunner implements ApplicationRunner {

    @Autowired
    private LicenseProperties licenseProperties;

    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private ScheduledFuture<?> scheduledFuture;

    // 启动定时任务
    public void startTimer() {
        scheduledFuture = scheduler.scheduleAtFixedRate(this::timer, 0, 30, TimeUnit.SECONDS);
        scheduler.scheduleAtFixedRate(this::fileTimer, 0, 60, TimeUnit.SECONDS);
    }

    // 停止定时任务
    public void stopTimer() {
        if (scheduledFuture != null) {
            scheduledFuture.cancel(false);
        }
    }

    /**
     * 文件唯一身份标识 == 相当于人类的指纹一样
     */
    private static String md5 = "";
    private static boolean isLoad = true;

    public static void LicenseVerifyInstall(LicenseProperties licenseVerifyProperties) {
        new LicenseCheckRunner(licenseVerifyProperties);
    }

    public LicenseCheckRunner(LicenseProperties licenseProperties) {
        // startTimer();
        this.licenseProperties = licenseProperties;
        if (StrUtil.isNotEmpty(licenseProperties.getLicensePath())) {
//            install();
            try {
                String readMd5 = getMd5(licenseProperties.getLicensePath());
                isLoad = true;
                if (LicenseCheckRunner.md5 == null || "".equals(LicenseCheckRunner.md5)) {
                    LicenseCheckRunner.md5 = readMd5;
                }
            } catch (Exception e) {

            }
        }
    }


    /**
     * 5秒检测一次，不能太快也不能太慢
     */
    protected void timer() {
        if (!isLoad) {
            return;
        }
        String readMd5 = null;
        try {
            readMd5 = getMd5(licenseProperties.getLicensePath());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 不相等，说明lic变化了
        if (!readMd5.equals(LicenseCheckRunner.md5)) {
            log.info("定时任务：检测到证书有变化");
            install();
            LicenseCheckRunner.md5 = readMd5;
        }
    }

    /**
     * 5秒检测一次，不能太快也不能太慢
     */
    protected void fileTimer() {
        createAndVerifyTimeFile();
    }

    private void install() {
        log.info("++++++++ 开始安装证书 ++++++++");
        LicenseVerifyParam param = new LicenseVerifyParam();
        param.setSubject(licenseProperties.getSubject());
        param.setPublicAlias(licenseProperties.getPublicAlias());
        param.setStorePass(licenseProperties.getStorePass());
        // 相对路径resources资源目录
        String resourcePath = getClass().getClassLoader().getResource("").getPath();
        // 证书地址
//        param.setLicensePath(resourcePath + "license.lic");
        param.setLicensePath(licenseProperties.getLicensePath());
        // 公钥地址
//        param.setPublicKeysStorePath(resourcePath + "publicCerts.keystore");
        param.setPublicKeysStorePath(licenseProperties.getPublicKeysStorePath());
        // 安装证书
        LicenseVerify licenseVerify = new LicenseVerify();
        licenseVerify.install(param);
        log.info("++++++++ 证书安装结束 ++++++++");
    }

    private void generate() {
        try {
            LicenseCheckModel serverInfos = AbstractServerInfos.getServer("").getServerInfos();

            //对象序列化为json
            String jsonString = serverInfos.toJsonString();
//            System.out.println("原始数据 = " + jsonString);
            //加密
            String encryptedJson = LicenseUtils.encrypt(jsonString);
            System.out.println("加密后 = " + encryptedJson);
            // 解密
            String decryptedJson = LicenseUtils.decrypt(encryptedJson);
//            System.out.println("解密后 = " + decryptedJson);
            // 比较解密后的字符串与原始字符串
            if (jsonString.equals(decryptedJson)) {
            } else {
                log.info("解密后的字符串与原始字符串不同");
            }

            // 断言
            assert jsonString.equals(decryptedJson) : "解密后的字符串与原始字符串不匹配";

            // 反序列化为对象
            ObjectMapper mapper = new ObjectMapper();
            LicenseCheckModel licenseParam = null;
            licenseParam = mapper.readValue(decryptedJson, LicenseCheckModel.class);
            // 标记为null的字段需要特殊处理
//        if ("null".equals(licenseParam.getRegisterAmount())) {
//            licenseParam.setRegisterAmount(null);
//        }
            FileWriter writer = new FileWriter(licenseProperties.getCodePath());
            writer.write(encryptedJson);
            writer.close();
            log.debug("licenseParam {}", licenseParam);
        } catch (Exception e) {
            log.error("生成激活码失败", e);
        }
    }

    private void createAndVerifyTimeFile() {
        boolean isCreate = false;
        String decryptedJson = "";
        try {
            // 取出时间文件
            BufferedReader reader = new BufferedReader(new FileReader(LicenseConstants.TIME_PATH + LicenseConstants.TIME_FILE_NAME));
            String fileJson = reader.readLine();
            reader.close();
            decryptedJson = LicenseUtils.decrypt(fileJson);
        } catch (Exception e) {
            isCreate = true;
            log.debug("first read time file error {}", e.getMessage());
        }
        if (StrUtil.isBlank(decryptedJson)) {
            isCreate = true;
        }
        if (isCreate) {
            // 不存在则创建
            FileUtil.mkdir(LicenseConstants.TIME_PATH);
            updateTimeFile();
        } else {
            // 存在则验证时间是否被篡改，不被篡改则更新当前时间
            if (Long.parseLong(decryptedJson) < System.currentTimeMillis()) {
                updateTimeFile();
            }
        }
    }

    private void updateTimeFile() {
        try {
            String currentTime = System.currentTimeMillis() + "";
            String encryptedJson = LicenseUtils.encrypt(currentTime);
            FileWriter writer = new FileWriter(LicenseConstants.TIME_PATH + LicenseConstants.TIME_FILE_NAME);
            writer.write(encryptedJson);
            writer.close();
        } catch (Exception e) {
            log.error("license create temp error", e);
        }
    }

    /**
     * <p>获取文件的md5</p>
     */
    public String getMd5(String filePath) throws Exception {
        File file;
        String md5 = "";
        try {
            file = new File(filePath);
            if (file.exists()) {
                FileInputStream is = new FileInputStream(file);
                byte[] data = new byte[is.available()];
                is.read(data);
                md5 = calculateMD5(data);
                is.close();
            }
        } catch (FileNotFoundException e) {
            log.error("找不到证书文件 {}", e.getMessage());
        }
        return md5;
    }

    public static String calculateMD5(byte[] data) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hashBytes = md.digest(data);

            StringBuilder hexString = new StringBuilder();
            for (byte b : hashBytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }

            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            log.error("证书文件计算MD5失败 {}", e.getMessage());
            return null;
        }
    }


    @Override
    public void run(ApplicationArguments args) throws Exception {
        generate();
        createAndVerifyTimeFile();
        install();
        startTimer();
    }
}