package cn.reghao.autodop.dmaster.app.service;

import cn.reghao.jdkutil.result.ResultStatus;
import cn.reghao.autodop.dmaster.app.db.query.config.AppConfigQuery;
import cn.reghao.autodop.dmaster.app.model.po.AppBuilding;
import cn.reghao.autodop.dmaster.app.model.po.AppDeploying;
import cn.reghao.autodop.dmaster.app.model.po.log.*;
import cn.reghao.autodop.dmaster.spring.interceptor.AppIntegrateReinitInterceptor;
import cn.reghao.autodop.dmaster.app.db.repository.AppBuildingRepository;
import cn.reghao.autodop.dmaster.app.db.repository.AppDeployingRepository;
import cn.reghao.autodop.dmaster.app.db.repository.log.BuildLogRepository;
import cn.reghao.autodop.dmaster.app.service.bd.AppIntegrate;
import cn.reghao.autodop.dmaster.app.service.bd.AppDeployer;
import cn.reghao.autodop.dmaster.app.model.po.config.AppConfig;
import cn.reghao.autodop.dmaster.app.service.bd.BuildNotifyMsg;
import cn.reghao.autodop.dmaster.notification.model.po.NotifyGroup;
import cn.reghao.autodop.dmaster.notification.model.po.NotifyType;
import cn.reghao.autodop.dmaster.notification.service.NotifyService;
import cn.reghao.autodop.common.util.thread.ThreadPoolWrapper;
import cn.reghao.autodop.dmaster.notification.service.notifier.ding.DingMsg;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.*;

/**
 * 应用构建部署分发器
 * 多个线程访问同一个 BuildDeployDispatcher 对象
 *
 * @author reghao
 * @date 2019-11-12 17:20:27
 */
@Slf4j
@Service
public class BuildDeployDispatcher {
    private AppBuildingRepository appBuildingRepository;
    private AppDeployingRepository appDeployingRepository;
    private BuildLogRepository buildLogRepository;
    private AppIntegrateReinitInterceptor reinitInterceptor;
    // TODO 多线程访问的集合
    private Set<String> onBuilding = new ConcurrentSkipListSet<>();
    private Set<String> onDeploying = new ConcurrentSkipListSet<>();
    private Map<String, AppIntegrate> integrateMap = new ConcurrentHashMap<>();
    private AppConfigQuery appConfigQuery;
    private ExecutorService threadPool;
    private AppDeployer appDeployer;
    private NotifyService notifyService;

    public BuildDeployDispatcher(AppBuildingRepository appBuildingRepository,
                                 AppDeployingRepository appDeployingRepository,
                                 AppIntegrateReinitInterceptor reinitInterceptor,
                                 AppConfigQuery appConfigQuery,
                                 BuildLogRepository buildLogRepository,
                                 NotifyService notify,
                                 AppDeployer appDeployer) {
        this.appBuildingRepository = appBuildingRepository;
        this.appDeployingRepository = appDeployingRepository;
        this.reinitInterceptor = reinitInterceptor;
        this.appConfigQuery = appConfigQuery;
        this.buildLogRepository = buildLogRepository;
        this.notifyService = notify;
        this.appDeployer = appDeployer;
        this.threadPool = ThreadPoolWrapper.threadPool("build-deploy");
    }

    /**
     * 构建部署应用
     *
     * @param
     * @return
     * @date 2021-02-06 上午1:46
     */
    public String buildAndDeploy(String appId, boolean isDeploy) throws Exception {
        if (!onBuilding.add(appId)) {
            return appId + " 正在构建中";
        }

        /* TODO 后面再使用缓存
        AppIntegrate appIntegrate = integrateMap.get(appId);
        if (appIntegrate != null) {
            if (reinitInterceptor.isAppChanged(appId)) {
                AppOrchestration app = appQuery.findByAppId(appId);
                appIntegrate.reInit(app);
            }
        } else {
            integrateMap.put(appId, appIntegrate);
        }*/

        AppConfig app = appConfigQuery.findByAppId(appId);
        if (app == null) {
            onBuilding.remove(appId);
            throw new Exception(appId + " 不存在");
        }
        AppIntegrate appIntegrate = new AppIntegrate(app);

        BuildSupplier supplier = new BuildSupplier(appIntegrate);
        List<NotifyGroup> notifyGroups = app.getNotifyGroups();
        log.info("开始异步构建 {}", appId);
        CompletableFuture.supplyAsync(supplier, threadPool)
                .whenComplete((buildLog, throwable) -> {
                    onBuilding.remove(appId);
                    if (throwable != null) {
                        String msg = throwable.getMessage();
                        return;
                    }

                    buildLog = buildLogRepository.save(buildLog);
                    AppBuilding appBuilding = appBuildingRepository.findByAppId(appId);
                    if (appBuilding != null) {
                        appBuilding.update(buildLog);
                        appBuildingRepository.save(appBuilding);
                    }

                    List<AppDeploying> appDeployings = appDeployingRepository.findByAppId(appId);
                    if (!appDeployings.isEmpty()) {
                        for (AppDeploying appDeploying : appDeployings) {
                            appDeploying.setBuildLogId(buildLog.getId());
                        }
                        appDeployingRepository.saveAll(appDeployings);
                    }
                    log.info("{} 构建完成", appId);
                })
                .thenAccept(buildLog -> {
                    if (!isDeploy) {
                        // TODO 抛出异常时不会有任何错误
                        notifyGroups.forEach(notifyGroup -> buildNotify(notifyGroup, buildLog));
                    }

                    if (buildLog.getResult().getCode() == ResultStatus.SUCCESS.getCode() && isDeploy) {
                        log.info("开始部署 {}", appId);
                        try {
                            appDeployer.deploy(buildLog);
                        } catch (MqttException e) {
                            e.printStackTrace();
                        }
                    }
                });
        return appId + " 已开始构建";
    }

    private void buildNotify(NotifyGroup notifyGroup, BuildLog buildLog) {
        String notifyType = notifyGroup.getNotifyType();
        switch (NotifyType.valueOf(notifyType)) {
            case ding:
                DingMsg dingMsg = new BuildNotifyMsg(buildLog).dingMsg();
                notifyService.notify(notifyGroup, dingMsg);
                break;
            case email:
                break;
            case sms:
                break;
            default:
                log.error("通知类型不存在");
        }
    }

    /**
     * 部署应用的最新版本
     *
     * @param
     * @return
     * @date 2021-02-06 上午2:02
     */
    private void deployApp(BuildLog buildLog) {
    }

    /**
     * 部署应用
     *
     * @param
     * @return
     * @date 2021-02-25 下午10:35
     */
    public void deploy(String appId, String commitId) {
    }

    public Map<String, List<String>> current() {
        Map<String, List<String>> map = new HashMap<>();
        map.put("正在构建", new ArrayList<>(onBuilding));
        map.put("正在部署", new ArrayList<>(onDeploying));
        return map;
    }

    public void clear() {
        onBuilding.clear();
        onDeploying.clear();
    }
}
