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

import cn.reghao.autodop.common.util.ByteConverter;
import cn.reghao.autodop.common.util.ByteType;
import cn.reghao.autodop.common.util.FileOps;
import cn.reghao.autodop.dmaster.app.db.crud.config.BuildDirCrud;
import cn.reghao.autodop.dmaster.app.db.query.config.BuildDirQuery;
import cn.reghao.autodop.dmaster.app.model.po.config.build.BuildDir;
import cn.reghao.autodop.dmaster.app.model.po.config.build.LocalBuildDir;
import cn.reghao.autodop.dmaster.app.service.config.BuildDirService;
import cn.reghao.jdkutil.machine.Disk;
import cn.reghao.jdkutil.machine.MachineId;
import cn.reghao.jdkutil.result.Result;
import cn.reghao.jdkutil.result.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.Objects;

/**
 * @author reghao
 * @date 2021-09-17 15:56:49
 */
@Slf4j
@Service
public class BuildDirServiceImpl implements BuildDirService {
    private final BuildDirQuery buildDirQuery;
    private final BuildDirCrud buildDirCrud;
    private final ByteConverter converter;

    public BuildDirServiceImpl(BuildDirQuery buildDirQuery, BuildDirCrud buildDirCrud) {
        this.buildDirQuery = buildDirQuery;
        this.buildDirCrud = buildDirCrud;
        this.converter = new ByteConverter();
    }

    @Override
    public void createAndSave() {
        try {
            BuildDir buildDir = defaultLocalBuildDir();
            buildDirCrud.save(buildDir);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("创建本地构建目录失败: {}，autodop-dmaster 结束运行", e.getMessage());
            System.exit(1);
        }
    }

    private BuildDir defaultLocalBuildDir() throws Exception {
        String home = System.getProperty("user.home");
        String localBuildDir = String.format("%s/autodop", home);
        return createLocalBuildDir(localBuildDir);
    }

    private BuildDir createLocalBuildDir(String localBuildDir) throws Exception {
        Disk.DiskDetail diskDetail = Disk.diskDetail(localBuildDir);
        BuildDir buildDir = new BuildDir();
        buildDir.setMachineId(MachineId.id());
        buildDir.setMachineIpv4(MachineId.ipv4());
        buildDir.setDirPath(localBuildDir);
        buildDir.setMountedOn(diskDetail.getMountedOn());
        buildDir.setTotal(converter.convert(ByteType.Bytes, diskDetail.getSize()));
        buildDir.setUsed(converter.convertStr(ByteType.Bytes, ByteType.MiB, diskDetail.getUsed()));
        buildDir.setAvail(converter.convertStr(ByteType.Bytes, ByteType.MiB, diskDetail.getAvail()));
        buildDir.setIsDelete(false);

        createLocalBuildDir(buildDir);
        return buildDir;
    }

    private void createLocalBuildDir(BuildDir buildDir) throws Exception {
        setLocalBuildDir(buildDir);
        File localRepo = new File(LocalBuildDir.localRepo);
        if (!localRepo.exists() && !FileOps.mkdirs(localRepo)) {
            throw new Exception(localRepo.getAbsolutePath() + " 不存在且创建失败");
        }

        File compileDir = new File(LocalBuildDir.compileDir);
        if (!compileDir.exists() && !FileOps.mkdirs(compileDir)) {
            throw new Exception(compileDir.getAbsolutePath() + " 不存在且创建失败");
        }

        File packDir = new File(LocalBuildDir.packDir);
        if (!packDir.exists() && !FileOps.mkdirs(packDir)) {
            throw new Exception(packDir.getAbsolutePath() + " 不存在且创建失败");
        }
    }

    private void setLocalBuildDir(BuildDir buildDir) {
        LocalBuildDir.localRepo = buildDir.getDirPath() + "/local-repo";
        LocalBuildDir.compileDir = buildDir.getDirPath() + "/compile-dir";
        LocalBuildDir.packDir = buildDir.getDirPath() + "/pack-dir";
    }

    @Override
    public void refresh(BuildDir buildDir) {
        setLocalBuildDir(buildDir);
        String dirPath = buildDir.getDirPath();
        Disk.DiskDetail diskDetail = Disk.diskDetail(dirPath);
        if (diskDetail != null) {
            buildDir.setUsed(converter.convertStr(ByteType.Bytes, ByteType.MiB, diskDetail.getUsed()));
            buildDir.setAvail(converter.convertStr(ByteType.Bytes, ByteType.MiB, diskDetail.getAvail()));
        } else {
            log.error("本地目录 {} 不合法，autodop-dmaster 结束运行", dirPath);
            System.exit(1);
        }
    }

    @Override
    public synchronized Result modify(BuildDir buildDir) {
        if (buildDir.getId() == null) {
            return Result.result(ResultStatus.FAIL, "构建目录不存在");
        }

        BuildDir oldBuildDir = buildDirQuery.findByMachineId(buildDir.getMachineId());
        String oldDirPath = oldBuildDir.getDirPath();
        String oldMountedOn = oldBuildDir.getMountedOn();
        String newDirPath = buildDir.getDirPath();
        String newMountedOn = Disk.diskDetail(newDirPath).getMountedOn();

        if (newDirPath.equals(oldDirPath)) {
            return Result.result(ResultStatus.FAIL, "目录没有改变");
        } else if (new File(newDirPath).exists()) {
            return Result.result(ResultStatus.FAIL, "新目录已存在，请另外指定一个不存在的目录");
        } else if (newMountedOn.equals(oldMountedOn)) {
            return Result.result(ResultStatus.FAIL, "新目录和当前目录同属一个分区");
        }

        String oldPackDir = LocalBuildDir.packDir;
        // TODO 目录迁移时不能有应用处于构建过程中
        try {
            BuildDir newBuildDir = createLocalBuildDir(newDirPath);
            File file = new File(oldPackDir);
            if (Objects.requireNonNull(file.list()).length != 0) {
                try {
                    FileOps.copyDirContentToDir(oldPackDir, LocalBuildDir.packDir);
                } catch (IOException e) {
                    FileOps.delete(new File(newDirPath));
                    return Result.result(ResultStatus.ERROR, e.getMessage());
                }
            }

            buildDirCrud.save(newBuildDir);
            return Result.result(ResultStatus.SUCCESS);
        } catch (Exception e) {
            return Result.result(ResultStatus.FAIL, e.getMessage());
        }
    }

    @Override
    public BuildDir get(String machineId) {
        return buildDirQuery.findByMachineId(machineId);
    }
}
