package com.docus.server.common;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.docus.core.util.Func;
import com.docus.core.util.ListUtils;
import com.docus.core.util.StopWatch;
import com.docus.core.util.json.JSON;
import com.docus.server.api.scheduling.management.SchCollectorApi;
import com.docus.server.api.scheduling.management.SchCollectorConfigApi;
import com.docus.server.common.download.downLoader.HttpDownloader;
import com.docus.server.enums.ConfigTypeEnum;
import com.docus.server.vo.scheduling.management.schcollector.LoadSchCollectorVO;
import com.docus.server.vo.scheduling.management.schcollectorconfig.SchCollectorConfigVO;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * load collector package and config
 */
@Component
@Slf4j
public class LoadPackageCommandLineRunner implements CommandLineRunner {
    @Resource
    private SchCollectorApi schCollectorApi;
    @Resource
    private SchCollectorConfigApi schCollectorConfigApi;

    @Value("${docus.collector-package-download-savePath:H://packages/}")
    private String saveCollectorPackagePath;

    @Value("${docus.collector-package-download-url}")
    private String collectorPackageDownloadUrl;

    private final static Lock DOWNLOAD_LOCK = new ReentrantLock();

    @Resource
    private StringRedisTemplate template;
    @Resource
    private Environment env;

    @Override
    public void run(String... args) throws Exception {

        loadPackages();

        loadConfig();

    }

    private void loadConfig() {
        List<SchCollectorConfigVO> schCollectorConfigVOList = schCollectorConfigApi.findAll();

        List<SchCollectorConfigVO> publicConfig = schCollectorConfigVOList
            .stream()
            .filter(p -> ConfigTypeEnum.PUBLIC_CONFIG.equals(p.getConfigType()))
            .collect(Collectors.toList());

        List<SchCollectorConfigVO> privateConfig = schCollectorConfigVOList
            .stream()
            .filter(p -> ConfigTypeEnum.PRIVATE_CONFIG.equals(p.getConfigType()))
            .collect(Collectors.toList());

        Map<Long, List<SchCollectorConfigVO>> privateConfigMap = ListUtils.groupBy(privateConfig, SchCollectorConfigVO::getCollectorId);


        Map<String, String> publicConfigMap = JSON.fromJSONWithGeneric(publicConfig.get(0).getConfigJson(), new TypeReference<Map<String, String>>() {
        });

        privateConfigMap.forEach((key, value) -> {
            try {
                if (Func.isNotEmpty(value)) {

                    SchCollectorConfigVO schCollectorConfigVO = value.get(0);

                    String privateConfigJson = schCollectorConfigVO.getConfigJson();

                    Map<String, String> privateConfigJsonMap = JSON.fromJSONWithGeneric(privateConfigJson, new TypeReference<Map<String, String>>() {
                    });

                    privateConfigJsonMap.putAll(publicConfigMap);

                    File file = new File(saveCollectorPackagePath + schCollectorConfigVO.getCollectorId() + "\\collector.json");
                    if (file.exists()) {

                        FileWriter fileWriter = new FileWriter(file);
                        fileWriter.write("");
                        fileWriter.flush();
                        fileWriter.close();

                        log.info("文件已经存在");

                    } else {

                        boolean result = file.createNewFile();
                        log.info("创建新文件是否成功：" + result + "，文件路径为：" + file.getPath());
                    }

                    PrintWriter writer = new PrintWriter(file, "UTF-8");
                    writer.println(JSON.toJSON(privateConfigJsonMap));
                    writer.flush();
                    writer.close();
                }
            } catch (Exception ex) {
                log.error(ex.getMessage(), ex);
            }
        });
    }

    private void loadPackages() throws Exception {
        //先从redis获取key，若没有，则全部更新，若有，局部更新

        String key = "docus:collectorsPackagesDownload:" + env.getProperty("server.port");
        String value = template.opsForValue().get(key);

        List<LoadSchCollectorVO> loadSchCollectorVOList = schCollectorApi.findAll();

        if (Func.isBlank(value)) {
            updateAll(loadSchCollectorVOList);
        } else {
            updatePart(loadSchCollectorVOList, value);
        }

        loadSchCollectorVOList.forEach(p -> {
            p.setDeployPath(saveCollectorPackagePath + p.getCollectorId() + "\\collector");
        });

        //根据服务器端口区分，每次更新了哪些采集器版本
        template.opsForValue().set(key, JSON.toJSON(loadSchCollectorVOList));
    }

    private void updatePart(List<LoadSchCollectorVO> loadSchCollectorVOList, String packageValue) throws Exception {
        List<LoadSchCollectorVO> redisLoadSchCollectors = JSON.fromJSONWithGeneric(packageValue, new TypeReference<List<LoadSchCollectorVO>>() {
        });

        Map<Long, LoadSchCollectorVO> collectorVoMap = ListUtils.toMap(redisLoadSchCollectors, LoadSchCollectorVO::getCollectorId);

        StopWatch watch = new StopWatch();

        for (LoadSchCollectorVO loadSchCollectorVO : loadSchCollectorVOList) {
            if (collectorVoMap.containsKey(loadSchCollectorVO.getCollectorId())) {
                LoadSchCollectorVO redisLoadSchCollectorVO = collectorVoMap.get(loadSchCollectorVO.getCollectorId());
                if (!redisLoadSchCollectorVO.getCollectorVersionId().equals(loadSchCollectorVO.getCollectorVersionId())) {
                    downloadCollectorPackages(loadSchCollectorVO);
                }
            }
        }

        log.info("终端局部下载采集器更新包耗时:{}ms", watch.elapsedTime());
    }

    private void updateAll(List<LoadSchCollectorVO> loadSchCollectorVOList) throws Exception {
        StopWatch watch = new StopWatch();
        for (LoadSchCollectorVO loadSchCollectorVO : loadSchCollectorVOList) {
            downloadCollectorPackages(loadSchCollectorVO);
        }
        log.info("终端全量下载采集器更新包耗时:{}ms", watch.elapsedTime());
    }

    private void downloadCollectorPackages(LoadSchCollectorVO loadSchCollectorVO) throws Exception {
        DOWNLOAD_LOCK.lock();
        try {
            //下载更新包
            HttpDownloader httpDownloader = new HttpDownloader(null);
            String url = collectorPackageDownloadUrl + "/sch/file/download?filePath=" + loadSchCollectorVO.getFilePath();
            String fileName = "collector.zip";
            String savePath = saveCollectorPackagePath + loadSchCollectorVO.getCollectorId(); //部署路径
            String procName = loadSchCollectorVO.getProcessName(); //进程名称

            httpDownloader.downLoadFromUrl(url, fileName, savePath);

            //解压更新压缩包
            ZipUtil.unzip(savePath + "\\" + fileName, savePath + "\\collector");

            //删除更新压缩包
            FileUtil.del(savePath + "\\" + fileName);
        } finally {
            DOWNLOAD_LOCK.unlock();
        }

    }
}
