package cn.reghao.bnt.web.devops.app.service.bd.impl;

import cn.reghao.bnt.web.admin.model.po.User;
import cn.reghao.bnt.web.devops.app.db.query.AppBuildQuery;
import cn.reghao.bnt.web.devops.app.model.po.AppBuilding;
import cn.reghao.bnt.web.devops.app.model.po.config.AppConfig;
import cn.reghao.bnt.web.devops.app.model.vo.AppTask;
import cn.reghao.bnt.web.devops.app.model.vo.BuildTask;
import cn.reghao.bnt.web.devops.app.service.AppBuildService;
import cn.reghao.bnt.web.devops.app.service.PermissionCheck;
import cn.reghao.bnt.web.devops.app.service.bd.BuildApp;
import cn.reghao.bnt.web.devops.app.service.bd.BuildDeployNotify;
import cn.reghao.bnt.web.devops.app.service.bd.DeployApp;
import cn.reghao.bnt.web.devops.builder.chain.*;
import cn.reghao.bnt.web.devops.builder.chain.impl.BuildChainParam;
import cn.reghao.bnt.web.devops.builder.chain.impl.BuildChainResult;
import cn.reghao.bnt.web.devops.builder.model.AppDto;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.jutil.jdk.web.result.ResultStatus;
import cn.reghao.jutil.jdk.thread.ThreadPoolWrapper;
import cn.reghao.jutil.jdk.string.IdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author reghao
 * @date 2023-02-24 21:17:44
 */
@Slf4j
@Service
public class BuildAppImpl implements BuildApp {
    private final IdGenerator idGenerator = new IdGenerator(10, "build-log-id");
    private final ExecutorService buildPool = ThreadPoolWrapper.threadPool("build", 10);
    private final Map<String, AppTask> taskMap = new HashMap<>(10);
    private final AppBuildService appBuildService;
    private final AppBuildQuery appBuildQuery;
    private final DeployApp deployApp;
    private final BuildDeployNotify buildDeployNotify;
    private final List<Handler<BuildChainParam, BuildChainResult>> list;
    private final PermissionCheck permissionCheck;

    public BuildAppImpl(AppBuildService appBuildService, AppBuildQuery appBuildQuery, DeployApp deployApp,
                        BuildDeployNotify buildDeployNotify, PermissionCheck permissionCheck) {
        this.appBuildService = appBuildService;
        this.appBuildQuery = appBuildQuery;
        this.deployApp = deployApp;
        this.buildDeployNotify = buildDeployNotify;
        this.list = BuildHandlers.buildHandlerList();
        this.permissionCheck = permissionCheck;
    }

    @Override
    public Result buildAndDeploy(String appId, boolean deploy) throws Exception {
        if (appBuildQuery.isBuilding(appId)) {
            String msg = String.format("%s 正在构建中", appId);
            return Result.result(ResultStatus.FAIL, msg);
        }

        if (taskMap.size() >= 10) {
            String msg = String.format("已有 %s 个 app 正在构建中", taskMap.size());
            return Result.result(ResultStatus.FAIL, msg);
        }

        AppConfig appConfig = appBuildQuery.getAppConfig(appId);
        if (appConfig == null) {
            throw new Exception(appId + " 不存在");
        }

        User user = permissionCheck.check(appConfig.getEnv());
        String buildBy = user.getScreenName();
        AppDto appDto = appConfig.getAppDto();
        localBuild(appDto, buildBy, deploy);
        return Result.success("构建部署任务已提交");
    }

    private void localBuild(AppDto appDto, String buildBy, boolean deploy) throws Exception {
        BuildTools.init(appDto);
        BuildChainParam buildParam = new BuildChainParam(appDto, deploy);
        String buildLogId = idGenerator.stringId();
        BuildChainResult buildResult = new BuildChainResult(buildLogId, appDto, buildBy);
        Bootstrap<BuildChainParam, BuildChainResult> buildChain = new Bootstrap<>(list, buildParam, buildResult);

        String appId = appDto.getAppId();
        log.info("{} 开始异步构建", appId);
        appBuildService.updateBeforeBuild(appId, buildBy);
        CompletableFuture<HandlerResult> future = CompletableFuture.supplyAsync(buildChain, buildPool)
                .whenComplete((chainResult, throwable) -> {
                    log.info("{} 异步构建后处理", appId);
                    if (throwable != null) {
                        throwable.printStackTrace();
                    }

                    BuildChainResult buildChainResult = (BuildChainResult) chainResult;
                    Result result = buildChainResult.getResult();
                    AppBuilding appBuilding = appBuildService.updateAfterBuild(buildChainResult);
                    log.info("{} 异步构建完成", appId);
                    taskMap.remove(appId);
                    if (appBuilding == null) {
                        return;
                    }

                    if (result.getCode() == ResultStatus.SUCCESS.getCode() && deploy) {
                        deployApp.deploy(appBuilding);
                    } else {
                        buildDeployNotify.buildNotify(appBuilding);
                    }
                });
        taskMap.put(appId, new AppTask(appDto, future));
    }

    @Override
    public List<BuildTask> getBuildTasks() {
        ThreadPoolExecutor tpe = (ThreadPoolExecutor) buildPool;
        int active = tpe.getActiveCount();
        log.info("active task -> {}", active);

        List<BuildTask> list = new ArrayList<>();
        taskMap.forEach((appId, appTask) -> {
            AppDto appDto = appTask.getApp();
            if (appTask.getFuture().isDone()) {
                list.add(new BuildTask(appDto, "已完成"));
            } else {
                list.add(new BuildTask(appDto, "进行中"));
            }
        });

        return list;
    }

    @Override
    public synchronized void cancelTask(String appId) {
        AppTask appTask = taskMap.get(appId);
        if (appTask != null) {
            CompletableFuture<HandlerResult> future = appTask.getFuture();
            if (!future.isDone()) {
                future.cancel(true);
            }
        }
    }

    private void remoteBuild(AppDto appDto, String buildBy) {
    }
}
