package cn.reghao.autodop.dagent.app;

import cn.reghao.autodop.common.msg.rpc.dto.app.AppStatus;
import cn.reghao.autodop.common.msg.rpc.dto.app.DeployParam;
import cn.reghao.autodop.common.dagent.app.log.AppLogArgs;
import cn.reghao.autodop.common.dagent.app.log.LogConfig;
import cn.reghao.autodop.common.dagent.app.log.LogFile;
import cn.reghao.autodop.common.dagent.app.log.LogType;
import cn.reghao.autodop.common.docker.Docker;
import cn.reghao.autodop.common.docker.exception.DockerException;
import cn.reghao.autodop.common.docker.pojo.Config;
import cn.reghao.autodop.common.docker.pojo.HostConfig;
import cn.reghao.autodop.common.docker.pojo.State;
import cn.reghao.autodop.common.docker.pojo.result.ContainerInfo;
import cn.reghao.autodop.common.util.ExceptionUtil;
import cn.reghao.jdkutil.machine.MachineId;
import cn.reghao.jdkutil.converter.DateTimeConverter;
import cn.reghao.jdkutil.serializer.JsonConverter;
import cn.reghao.autodop.dagent.spring.DagentLifecycle;

import java.util.ArrayList;
import java.util.List;

/**
 * @author reghao
 * @date 2021-02-22 16:21:37
 */
public class DockerAppServiceImpl implements AppService {
    private final long sleep = 10_000;

    @Override
    public AppStatus deploy(DeployParam deployParam) throws DockerException {
        String appId = deployParam.getAppId();
        String packagePath = deployParam.getPackagePath();
        Config dockerConfig = JsonConverter.jsonToObject(deployParam.getStartScript(), Config.class);
        if (dockerConfig == null) {
            dockerConfig = new Config(packagePath);
        } else {
            dockerConfig.setImage(packagePath);
            if (dockerConfig.getHostConfig() == null) {
                dockerConfig.setHostConfig(new HostConfig());
            }
        }

        try (Docker docker = new Docker()) {
            docker.pull(packagePath);
            String containerId = docker.run(appId, dockerConfig);
            Thread.sleep(sleep);

            ContainerInfo containerInfo = docker.inspectContainer(containerId);
            return getAppStatus(appId, containerInfo);
        } catch (InterruptedException e) {
            throw new DockerException(ExceptionUtil.errorMsg(e));
        }
    }

    private AppStatus getAppStatus(String appId, ContainerInfo containerInfo) {
        AppStatus appStatus = new AppStatus();
        appStatus.setMachineId(MachineId.id());
        appStatus.setMachineIpv4(MachineId.ipv4());
        appStatus.setAppId(appId);
        appStatus.setPackagePath(containerInfo.getConfig().getImage());

        State state = containerInfo.getState();
        boolean isRunning = state.isRunning();
        appStatus.setIsRunning(isRunning);
        if (isRunning) {
            appStatus.setStartTime(DateTimeConverter.localDateTime(state.getStartedAt()));
            appStatus.setPid(state.getPid());
        } else {
            appStatus.setPid(-1);
        }
        return appStatus;
    }

    @Override
    public AppStatus status(String appId) throws DockerException {
        try (Docker docker = new Docker()) {
            String containerId = docker.getIdByName(appId);
            if (containerId == null) {
                throw new DockerException("没有和 " + appId + " 应用 ID关联的容器");
            }
            ContainerInfo containerInfo = docker.inspectContainer(containerId);
            return getAppStatus(appId, containerInfo);
        }
    }

    @Override
    public AppStatus restart(String appId) throws DockerException {
        try (Docker docker = new Docker()) {
            String containerId = docker.getIdByName(appId);
            docker.restart(containerId);
            Thread.sleep(sleep);

            ContainerInfo containerInfo = docker.inspectContainer(containerId);
            return getAppStatus(appId, containerInfo);
        } catch (InterruptedException e) {
            throw new DockerException(ExceptionUtil.errorMsg(e));
        }
    }

    @Override
    public AppStatus stop(String appId) throws DockerException {
        try (Docker docker = new Docker()) {
            String containerId = docker.getIdByName(appId);
            docker.stop(containerId);

            ContainerInfo containerInfo = docker.inspectContainer(containerId);
            return getAppStatus(appId, containerInfo);
        }
    }

    @Override
    public AppStatus start(String appId) throws DockerException {
        try (Docker docker = new Docker()) {
            String containerId = docker.getIdByName(appId);
            docker.start(containerId);
            Thread.sleep(sleep);

            ContainerInfo containerInfo = docker.inspectContainer(containerId);
            return getAppStatus(appId, containerInfo);
        } catch (InterruptedException e) {
            throw new DockerException(ExceptionUtil.errorMsg(e));
        }
    }

    @Override
    public List<LogFile> logFiles(AppLogArgs appLogArgs) throws DockerException {
        String appId = appLogArgs.getAppId();
        List<LogConfig> logConfigs = appLogArgs.getLogConfigs();

        try (Docker docker = new Docker()) {
            String containerId = docker.getIdByName(appId);
            List<LogFile> logFiles = new ArrayList<>();
            for (LogConfig logConfig : logConfigs) {
                String logType = logConfig.getType();
                String logLevel = logConfig.getLevel();
                String logPath = logConfig.getLogPath();

                LogFile logFile = new LogFile(logType, logLevel);
                switch (LogType.valueOf(logType)) {
                    case console:
                        logFile.setFiles(docker.fileList(containerId, logPath));
                        break;
                    case file:
                        logFile.setFiles(docker.fileList(containerId, logPath));
                        break;
                    default:
                        throw new DockerException("日志类型 " + logType + " 不存在");
                }
                logFiles.add(logFile);
            }
            return logFiles;
        }
    }

    @Override
    public List<String> logContent(AppLogArgs appLogArgs) throws DockerException {
        String appId = appLogArgs.getAppId();
        List<LogConfig> logConfigs = appLogArgs.getLogConfigs();
        LogConfig logConfig = logConfigs.get(0);
        String logType = logConfig.getType();
        String logLevel = logConfig.getLevel();
        String logPath = logConfig.getLogPath();

        try (Docker docker = new Docker()) {
            String containerId = docker.getIdByName(appId);
            switch (LogType.valueOf(logType)) {
                case console:
                    return docker.consoleLog(containerId, logLevel);
                case file:
                    return docker.fileLog(containerId, logPath);
                default:
                    throw new DockerException("日志类型 " + logType + " 不存在");
            }
        }
    }
}
