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

import cn.reghao.bnt.web.devops.app.db.repository.AppBuildingRepository;
import cn.reghao.bnt.web.devops.app.db.repository.config.AppConfigRepository;
import cn.reghao.bnt.web.devops.app.db.repository.log.BuildLogRepository;
import cn.reghao.bnt.web.devops.app.model.constant.BuildStatus;
import cn.reghao.bnt.web.devops.app.model.dto.AppConfigDto;
import cn.reghao.bnt.web.devops.app.model.dto.AppConfigUpdateDto;
import cn.reghao.bnt.web.devops.app.model.dto.CopyAppDto;
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.po.log.BuildLog;
import cn.reghao.bnt.web.devops.app.service.AppBuildService;
import cn.reghao.bnt.web.devops.app.service.AppDeployService;
import cn.reghao.bnt.web.devops.build.model.vo.BuildConfig;
import cn.reghao.bnt.web.devops.build.service.BuildConfigChecker;
import cn.reghao.bnt.web.devops.builder.chain.BuildTools;
import cn.reghao.bnt.web.devops.builder.chain.impl.BuildChainResult;
import cn.reghao.bnt.web.devops.builder.model.AppDto;
import cn.reghao.bnt.web.devops.builder.model.LocalBuildDir;
import cn.reghao.bnt.web.devops.builder.model.constant.RepoType;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.jutil.jdk.web.result.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;

/**
 * @author reghao
 * @date 2021-10-22 14:48:26
 */
@Slf4j
@Service
public class AppBuildServiceImpl implements AppBuildService {
    private final AppConfigRepository appConfigRepository;
    private final AppBuildingRepository buildingRepository;
    private final BuildLogRepository buildLogRepository;
    private final BuildConfigChecker buildConfigChecker;
    private final AppDeployService appDeployService;

    public AppBuildServiceImpl(AppConfigRepository appConfigRepository, AppBuildingRepository buildingRepository,
                               BuildLogRepository buildLogRepository, BuildConfigChecker buildConfigChecker,
                               AppDeployService appDeployService) {
        this.appConfigRepository = appConfigRepository;
        this.buildingRepository = buildingRepository;
        this.buildLogRepository = buildLogRepository;
        this.buildConfigChecker = buildConfigChecker;
        this.appDeployService = appDeployService;
    }

    @Override
    public Result createAppBuild(AppConfigDto appConfigDto) {
        String appId = appConfigDto.getAppId();
        AppConfig appConfig = appConfigRepository.findByAppId(appId);
        if (appConfig != null) {
            String msg = String.format("%s 已存在", appId);
            return Result.result(ResultStatus.FAIL, msg);
        }

        String appRepo = appConfigDto.getAppRepo();
        String appRepo1 = StringUtils.trimAllWhitespace(appRepo);
        appConfigDto.setAppRepo(appRepo1);

        String repoAuthName = appConfigDto.getRepoAuthConfig();
        String compilerName = appConfigDto.getCompilerConfig();
        String packerName = appConfigDto.getPackerConfig();
        try {
            BuildConfig buildConfig = buildConfigChecker.checkAndGet(repoAuthName, compilerName, packerName);
            String projDirname = getProjDirname(appConfigDto.getAppRepo(), buildConfig.getRepoAuth().getType());

            appConfig = new AppConfig(appConfigDto, projDirname, buildConfig);
            create(appConfig);
        } catch (Exception e) {
            return Result.result(ResultStatus.FAIL, e.getMessage());
        }

        return Result.result(ResultStatus.SUCCESS);
    }

    private String getProjDirname(String appRepo, String repoType) {
        int index = appRepo.lastIndexOf("/");
        String projDir;
        if (repoType.equals(RepoType.git.getName())) {
            projDir = appRepo.substring(index+1).replace(".git", "");
        } else {
            projDir = appRepo.substring(index+1);
        }

        return projDir;
    }

    @Transactional(rollbackFor = Exception.class)
    public void create(AppConfig appConfig) {
        appConfig = appConfigRepository.save(appConfig);
        AppBuilding appBuilding = new AppBuilding(appConfig);
        buildingRepository.save(appBuilding);
    }

    @Override
    public Result createByCopy(CopyAppDto copyAppDto) {
        String appId = copyAppDto.getAppId();
        AppConfig fromApp = appConfigRepository.findByAppId(appId);
        if (fromApp == null) {
            String msg = String.format("拷贝的 %s 不存在", appId);
            return Result.result(ResultStatus.FAIL, msg);
        }

        String newAppId = copyAppDto.getNewAppId();
        AppConfig toApp = appConfigRepository.findByDeletedFalseAndAppId(newAppId);
        if (toApp != null) {
            String msg = String.format("%s 已存在", newAppId);
            return Result.result(ResultStatus.FAIL, msg);
        }

        try {
            toApp = (AppConfig) fromApp.clone();
            toApp.setId(null);
            toApp.setDeleted(false);
            toApp.setCreateTime(LocalDateTime.now());
            toApp.setUpdateTime(LocalDateTime.now());
            toApp.setAppId(copyAppDto.getNewAppId());
            toApp.setAppName(copyAppDto.getNewAppId());
            toApp.setEnv(copyAppDto.getNewEnv());
            toApp.setRepoBranch(copyAppDto.getNewRepoBranch());
            toApp = appConfigRepository.save(toApp);

            AppBuilding appBuilding = new AppBuilding(toApp);
            buildingRepository.save(appBuilding);
            return Result.result(ResultStatus.SUCCESS);
        } catch (Exception e) {
            return Result.fail(e.getMessage());
        }
    }

    @Override
    public Result updateAppConfig(AppConfigUpdateDto appConfigUpdateDto) {
        String appId = appConfigUpdateDto.getAppId();
        AppConfig appConfig = appConfigRepository.findByAppId(appId);
        if (appConfig == null) {
            String msg = String.format("%s 不存在", appId);
            return Result.result(ResultStatus.FAIL, msg);
        }

        String repoBranch = appConfigUpdateDto.getRepoBranch();
        if (!repoBranch.equals(appConfig.getRepoBranch())) {
            Result result = clearLocalRepo(appId);
            if (result.getCode() != 0) {
                return result;
            }
        }

        String repoAuthName = appConfigUpdateDto.getRepoAuthConfig();
        String compilerName = appConfigUpdateDto.getCompilerConfig();
        String packerName = appConfigUpdateDto.getPackerConfig();
        try {
            BuildConfig buildConfig = buildConfigChecker.checkAndGet(repoAuthName, compilerName, packerName);
            appConfig.update(appConfigUpdateDto, buildConfig);
            appConfigRepository.save(appConfig);

            AppBuilding appBuilding1 = buildingRepository.findAllByAppConfig_AppId(appId);
            appBuilding1.setAppConfig(appConfig);
            buildingRepository.save(appBuilding1);
        } catch (Exception e) {
            return Result.result(ResultStatus.FAIL, e.getMessage());
        }

        return Result.result(ResultStatus.SUCCESS);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result deleteAppBuild(String appId) {
        Result result = appDeployService.deleteByApp(appId);
        if (result.getCode() != ResultStatus.SUCCESS.getCode()) {
            return result;
        }

        buildLogRepository.deleteByAppConfig_AppId(appId);
        buildingRepository.deleteByAppConfig_AppId(appId);
        appConfigRepository.deleteByAppId(appId);
        BuildTools.removeApp(appId);
        clearLocalRepo(appId);
        return Result.result(ResultStatus.SUCCESS);
    }

    @Transactional
    @Override
    public void deleteBuildLog(String buildLogId) {
        appDeployService.deleteDeployLog(buildLogId);
        buildLogRepository.deleteByBuildLogId(buildLogId);
    }

    @Override
    public void updateBeforeBuild(String appId, String buildBy) {
        AppBuilding appBuilding1 = buildingRepository.findAllByAppConfig_AppId(appId);
        appBuilding1.beforeBuild(buildBy);
        appBuilding1.setOnBuilding(true);
        appBuilding1.setCommitId(null);
        appBuilding1.setCommitTime(null);
        appBuilding1.setBuildTime(LocalDateTime.now());
        appBuilding1.setBuildResult(BuildStatus.onBuilding.getDesc());
        buildingRepository.save(appBuilding1);
    }

    @Override
    public AppBuilding updateAfterBuild(BuildChainResult buildChainResult) {
        AppDto appDto = buildChainResult.getAppDto();
        String appId = appDto.getAppId();
        AppConfig appConfig = appConfigRepository.findByAppId(appId);

        BuildLog buildLog = new BuildLog(buildChainResult, appConfig);
        AppBuilding appBuilding1 = buildingRepository.findAllByAppConfig_AppId(appId);
        try {
            buildLogRepository.save(buildLog);
            appBuilding1.afterBuild(buildLog);
            buildingRepository.save(appBuilding1);

            appDeployService.updateAfterBuild(appBuilding1);
            return appBuilding1;
        } catch (Exception e) {
            String errMsg = e.getMessage();
            // TODO errMsg 不应该在这里设置, 直接设置为构建失败即可
            appBuilding1.afterBuild(buildLog, errMsg);
            buildingRepository.save(appBuilding1);
            log.error("持久化 BuidlLog 抛出异常: {}", errMsg);
        }

        return null;
    }

    @Override
    public void resetBuildStat() {
        PageRequest pageRequest = PageRequest.of(0, 100);
        Page<AppBuilding> page = buildingRepository.findAllByOnBuildingIsTrue(pageRequest);
        if (page.hasContent()) {
            page.stream().forEach(appBuilding -> {
                appBuilding.setBuildCode(ResultStatus.SUCCESS.getCode());
                appBuilding.setBuildResult(BuildStatus.neverBuild.getDesc());
                appBuilding.setOnBuilding(false);
            });
            buildingRepository.saveAll(page.getContent());
        }
    }

    @Override
    public Result clearLocalRepo(String appId) {
        // TODO 保证 app 没有处在构建状态
        String localRepo = LocalBuildDir.localRepo + File.separator + appId;
        File dir = new File(localRepo);
        try {
            if (dir.exists()) {
                FileUtils.cleanDirectory(dir);
            }

            return Result.result(ResultStatus.SUCCESS);
        } catch (IOException e) {
            return Result.result(ResultStatus.FAIL, e.getMessage());
        }
    }
}
