package com.ximai.web.service;

import com.ximai.common.config.RuoYiConfig;
import com.ximai.common.core.domain.vo.GitBuildVersionVo;
import com.ximai.common.utils.data.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * 读取 Git 构建版本：优先 jar 内 git.properties（Maven prepare-package 生成），其次环境变量（CI），最后本机 git 命令。
 */
@Service
public class BuildVersionService {

    private static final Logger log = LoggerFactory.getLogger(BuildVersionService.class);

    private static final String GIT_PROPERTIES = "git.properties";

    private static final DateTimeFormatter BUILD_TIME_FMT =
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX").withZone(ZoneId.systemDefault());

    @Autowired
    private RuoYiConfig ruoYiConfig;

    public GitBuildVersionVo getGitBuildVersion() {
        Properties git = loadGitProperties();
        if (!hasGitInfo(git)) {
            fillFromEnvironment(git);
        }
        if (!hasGitInfo(git)) {
            fillFromGitCommand(git);
        }
        if (!hasGitInfo(git)) {
            log.warn("未获取到 Git 版本：请确认 admin.jar 内含有 git.properties（在含 .git 的 mes 目录执行 mvn clean package -pl admin -am）");
        }
        GitBuildVersionVo vo = new GitBuildVersionVo();
        vo.setApplication(ruoYiConfig.getName());
        vo.setProjectVersion(ruoYiConfig.getVersion());
        vo.setBranch(prop(git, "git.branch"));
        vo.setCommitId(prop(git, "git.commit.id.abbrev"));
        vo.setCommitIdFull(prop(git, "git.commit.id"));
        vo.setCommitMessage(prop(git, "git.commit.message.short"));
        vo.setCommitTime(prop(git, "git.commit.time"));
        vo.setBuildTime(prop(git, "git.build.time"));
        vo.setBuildVersion(prop(git, "git.build.version"));
        vo.setTags(prop(git, "git.tags"));
        return vo;
    }

    /**
     * 从 classpath 读取（Spring Boot fat jar 为 BOOT-INF/classes/git.properties）
     */
    private Properties loadGitProperties() {
        Properties properties = new Properties();
        InputStream in = null;
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl != null) {
            in = cl.getResourceAsStream(GIT_PROPERTIES);
        }
        if (in == null) {
            in = BuildVersionService.class.getClassLoader().getResourceAsStream(GIT_PROPERTIES);
        }
        if (in == null) {
            log.debug("classpath 中未找到 {}", GIT_PROPERTIES);
            return properties;
        }
        try (InputStream stream = in) {
            properties.load(stream);
            log.debug("已加载 git.properties，keys={}", properties.size());
        } catch (Exception e) {
            log.warn("读取 git.properties 失败: {}", e.getMessage());
        }
        return properties;
    }

    private static boolean hasGitInfo(Properties git) {
        if (git == null || git.isEmpty()) {
            return false;
        }
        String id = git.getProperty("git.commit.id");
        String abbrev = git.getProperty("git.commit.id.abbrev");
        return (StringUtils.isNotEmpty(id) && !"unknown".equalsIgnoreCase(id))
                || (StringUtils.isNotEmpty(abbrev) && !"unknown".equalsIgnoreCase(abbrev));
    }

    /** Jenkins/GitLab 等 CI 可在打包时注入环境变量 */
    private void fillFromEnvironment(Properties git) {
        putEnv(git, "git.branch", "GIT_BRANCH", "CI_COMMIT_REF_NAME", "BRANCH_NAME");
        putEnv(git, "git.commit.id", "GIT_COMMIT_ID", "CI_COMMIT_SHA", "GIT_COMMIT");
        putEnv(git, "git.commit.id.abbrev", "GIT_COMMIT_ID_ABBREV", "CI_COMMIT_SHORT_SHA");
        putEnv(git, "git.commit.message.short", "GIT_COMMIT_MESSAGE", "CI_COMMIT_MESSAGE");
        putEnv(git, "git.commit.time", "GIT_COMMIT_TIME");
        putEnv(git, "git.build.time", "BUILD_TIME", "CI_BUILD_TIME");
        putEnv(git, "git.build.version", "BUILD_VERSION");
        putEnv(git, "git.tags", "GIT_TAGS");
        if (!git.containsKey("git.build.time")) {
            git.setProperty("git.build.time", BUILD_TIME_FMT.format(Instant.now()));
        }
        if (!git.containsKey("git.build.version") && StringUtils.isNotEmpty(ruoYiConfig.getVersion())) {
            git.setProperty("git.build.version", ruoYiConfig.getVersion());
        }
        if (StringUtils.isNotEmpty(git.getProperty("git.commit.id"))
                && !git.containsKey("git.commit.id.abbrev")) {
            String full = git.getProperty("git.commit.id");
            git.setProperty("git.commit.id.abbrev",
                    full.length() > 7 ? full.substring(0, 7) : full);
        }
    }

    private static void putEnv(Properties git, String propKey, String... envKeys) {
        if (StringUtils.isNotEmpty(git.getProperty(propKey))) {
            return;
        }
        for (String envKey : envKeys) {
            String val = System.getenv(envKey);
            if (StringUtils.isNotEmpty(val)) {
                git.setProperty(propKey, val.trim());
                return;
            }
        }
    }

    private void fillFromGitCommand(Properties git) {
        File gitRoot = resolveMesGitRoot();
        if (gitRoot == null) {
            return;
        }
        String branch = gitExec(gitRoot, "rev-parse", "--abbrev-ref", "HEAD");
        String commitFull = gitExec(gitRoot, "rev-parse", "HEAD");
        String commitShort = gitExec(gitRoot, "rev-parse", "--short", "HEAD");
        String commitTime = gitExec(gitRoot, "log", "-1", "--format=%ci");
        String commitMessage = gitExec(gitRoot, "log", "-1", "--format=%s");
        String tags = gitExec(gitRoot, "describe", "--tags", "--always");

        if (StringUtils.isNotEmpty(branch)) {
            git.setProperty("git.branch", branch);
        }
        if (StringUtils.isNotEmpty(commitFull)) {
            git.setProperty("git.commit.id", commitFull);
        }
        if (StringUtils.isNotEmpty(commitShort)) {
            git.setProperty("git.commit.id.abbrev", commitShort);
        }
        if (StringUtils.isNotEmpty(commitTime)) {
            git.setProperty("git.commit.time", commitTime);
        }
        if (StringUtils.isNotEmpty(commitMessage)) {
            git.setProperty("git.commit.message.short", commitMessage);
        }
        if (StringUtils.isNotEmpty(tags) && !tags.contains("fatal")) {
            git.setProperty("git.tags", tags);
        }
        git.setProperty("git.build.time", BUILD_TIME_FMT.format(Instant.now()));
        if (StringUtils.isNotEmpty(ruoYiConfig.getVersion())) {
            git.setProperty("git.build.version", ruoYiConfig.getVersion());
        }
    }

    private static File resolveMesGitRoot() {
        File start = new File(System.getProperty("user.dir")).getAbsoluteFile();
        File dir = start;
        while (dir != null) {
            if (new File(dir, ".git").exists()) {
                return dir;
            }
            dir = dir.getParentFile();
        }
        File parent = start.getParentFile();
        if (parent != null && new File(parent, ".git").exists()) {
            return parent;
        }
        return null;
    }

    private static String gitExec(File gitRoot, String... command) {
        try {
            String[] cmd = new String[command.length + 1];
            cmd[0] = "git";
            System.arraycopy(command, 0, cmd, 1, command.length);
            Process process = new ProcessBuilder(cmd)
                    .directory(gitRoot)
                    .redirectErrorStream(true)
                    .start();
            boolean finished = process.waitFor(3, TimeUnit.SECONDS);
            if (!finished || process.exitValue() != 0) {
                return null;
            }
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            byte[] chunk = new byte[256];
            int n;
            InputStream in = process.getInputStream();
            while ((n = in.read(chunk)) != -1) {
                buf.write(chunk, 0, n);
            }
            String out = new String(buf.toByteArray(), StandardCharsets.UTF_8).trim();
            return StringUtils.isEmpty(out) ? null : out;
        } catch (Exception e) {
            log.debug("git {} 执行失败: {}", String.join(" ", command), e.getMessage());
            return null;
        }
    }

    private static String prop(Properties properties, String key) {
        String value = properties.getProperty(key);
        return StringUtils.isNotEmpty(value) ? value.trim() : "unknown";
    }
}
