package cn.reghao.bnt.web.devops.builder.chain;

import cn.reghao.bnt.common.docker.model.Config;
import cn.reghao.bnt.common.docker.model.DockerAuth;
import cn.reghao.bnt.common.docker.model.Volumes;
import cn.reghao.bnt.common.msg.constant.PackType;
import cn.reghao.bnt.web.devops.build.model.po.CompilerBind;
import cn.reghao.bnt.web.devops.builder.model.AppDto;
import cn.reghao.bnt.web.devops.builder.model.CompilerDto;
import cn.reghao.bnt.web.devops.builder.model.PackerDto;
import cn.reghao.bnt.web.devops.builder.model.RepoAuth;
import cn.reghao.bnt.web.devops.builder.model.constant.CompileType;
import cn.reghao.bnt.web.devops.builder.model.constant.RepoType;
import cn.reghao.bnt.web.devops.builder.tool.compiler.*;
import cn.reghao.bnt.web.devops.builder.tool.packer.CodePacker;
import cn.reghao.bnt.web.devops.builder.tool.packer.DockerPack;
import cn.reghao.bnt.web.devops.builder.tool.packer.StaticPack;
import cn.reghao.bnt.web.devops.builder.tool.packer.ZipPack;
import cn.reghao.bnt.web.devops.builder.tool.repo.CodeUpdater;
import cn.reghao.bnt.web.devops.builder.tool.repo.GitImpl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author reghao
 * @date 2022-05-07 14:35:11
 */
public class BuildTools {
    // appId -> {codeUpdate,codeCompiler,codePacker} -> name
    private static final Map<String, Map<String, String>> buildConfig = new HashMap<>();
    private static final Map<String, CodeUpdater> codeUpdaterMap = new HashMap<>();
    private static final Map<String, CodeCompiler> codeCompilerMap = new HashMap<>();
    private static final Map<String, CodePacker> codePackerMap = new HashMap<>();

    public static CodeUpdater getCodeUpdater(String appId) {
        String codeUpdater = buildConfig.get(appId).get("codeUpdater");
        return codeUpdaterMap.get(codeUpdater);
    }

    public static CodeCompiler getCodeCompiler(String appId) {
        String codeCompiler = buildConfig.get(appId).get("codeCompiler");
        return codeCompilerMap.get(codeCompiler);
    }

    public static CodePacker getCodePacker(String appId) {
        String codePacker = buildConfig.get(appId).get("codePacker");
        return codePackerMap.get(codePacker);
    }

    public static void removeApp(String appId) {
        buildConfig.remove(appId);
    }

    public static void removeCodeUpdater(String repoAuthName) {
        codeUpdaterMap.remove(repoAuthName);
    }

    public static void removeCodeCompiler(String compilerName) {
        codeCompilerMap.remove(compilerName);
    }

    public static void removeCodePacker(String packerName) {
        codePackerMap.remove(packerName);
    }

    public static void init(AppDto appDto) throws Exception {
        String appId = appDto.getAppId();
        RepoAuth repoAuth = appDto.getBuildConfigDto().getRepoAuth();
        DockerAuth dockerAuth = appDto.getBuildConfigDto().getDockerAuth();

        String repoAuthName = repoAuth.getName();
        CodeUpdater codeUpdater = codeUpdaterMap.get(repoAuthName);
        if (codeUpdater == null) {
            switch (RepoType.valueOf(repoAuth.getType())) {
                case git:
                    codeUpdater = new GitImpl(repoAuth);
                    break;
                default:
                    throw new Exception(repoAuth.getType() + " 仓库类型未实现");
            }
            codeUpdaterMap.put(repoAuthName, codeUpdater);
        }
        buildConfig.computeIfAbsent(appId, k -> new HashMap<>()).put("codeUpdater", repoAuthName);

        CompilerDto compilerDto = appDto.getBuildConfigDto().getCompilerDto();
        String compilerName = compilerDto.getName();
        CodeCompiler codeCompiler = codeCompilerMap.get(compilerName);
        if (codeCompiler == null) {
            switch (CompileType.valueOf(compilerDto.getType())) {
                case shell:
                    codeCompiler = new ShellCompiler(compilerDto);
                    break;
                case maven:
                    codeCompiler = new MavenCompiler(compilerDto.getHomePath());
                    break;
                case docker:
                    String compilerImage = compilerDto.getCompilerImage();
                    List<CompilerBind> compilerBinds = compilerDto.getCompilerBinds();
                    String compileCmd = compilerDto.getCompileCmd();
                    Config config = new Config(compilerImage);
                    config.setVolumes(new Volumes());
                    config.setCmd(List.of("sh", "-c", compileCmd));
                    compilerBinds.forEach(bind -> {
                        //config.getVolumes().getMap().put(bind.getHostPath(), bind.getContainerPath());
                        config.getVolumes().getMap().put(bind.getContainerPath(), bind.getHostPath());
                    });

                    codeCompiler = new DockerCompiler(config);
                    break;
                case none:
                    codeCompiler = new EmptyCompiler();
                    break;
                default:
                    throw new Exception(compilerDto.getType() + " 编译类型未实现");
            }
            codeCompilerMap.put(compilerName, codeCompiler);
        }
        buildConfig.computeIfAbsent(appId, k -> new HashMap<>()).put("codeCompiler", compilerName);

        PackerDto packerDto = appDto.getBuildConfigDto().getPackerDto();
        String packerName = packerDto.getName();
        CodePacker codePacker = codePackerMap.get(packerName);
        if (codePacker == null) {
            switch (PackType.valueOf(packerDto.getType())) {
                case docker:
                    if (dockerAuth != null) {
                        codePacker = new DockerPack(packerDto.getTargetPath(), dockerAuth);
                    } else {
                        codePacker = new DockerPack(packerDto.getTargetPath());
                    }

                    break;
                case staticFiles:
                    codePacker = new StaticPack(packerDto.getBinDirname());
                    break;
                default:
                    codePacker = new ZipPack(packerDto.getBinDirname());
                    break;
            }
            codePackerMap.put(packerName, codePacker);
        }
        buildConfig.computeIfAbsent(appId, k -> new HashMap<>()).put("codePacker", packerName);
    }
}
