package cn.axzo.workflow.client.config;

import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: liyong.tian
 * @Date: 2022/9/17
 * @Description:
 */
@EnableFeignClients(basePackages = {"cn.axzo.workflow.client.feign"})
@Configuration(proxyBeanMethods = false)
@Slf4j
public class WorkflowEngineClientAutoConfiguration {
    private static String POD_NAMESPACE;
    private static final Pattern POD_VERSION = Pattern.compile(".*-([\\d.]+(-.*)?)\\.jar!/");
    private static final Pattern JAR_SNAPSHOT_VERSION = Pattern.compile("\\d+\\.\\d+\\.\\d+");
    private static final Pattern JAR_RELEASE_VERSION = Pattern.compile(".*-([\\d.]+(-.*)?)\\.jar");
    private static final Pattern POM_VERSION = Pattern.compile("<parent>.*<version>(\\S+)</version>.*</parent>",
            Pattern.DOTALL);

    @Bean
    public String serviceVersion() {
        Map<String, String> env = System.getenv();
        if (env != null) {
            POD_NAMESPACE = env.get("MY_POD_NAMESPACE");
        }

        String version = null;
        CodeSource source = this.getClass().getProtectionDomain().getCodeSource();
        URL location = source.getLocation();
        try {
            if (StringUtils.hasText(POD_NAMESPACE)) {
                version = getVersionFromPod(location);
            } else {
                version = getVersion(location);
            }
        } catch (Exception e) {
            log.error("get version error: {}", e.getMessage(), e);
        }

        String serviceVersion = Objects.isNull(version) ? "1.2.0-SNAPSHOT" : version;
        log.info("client current version: {}", serviceVersion);
        return serviceVersion;
    }

    private String getVersionFromPod(URL location) throws URISyntaxException {
        try {
            Path path = Paths.get(location.toURI());
            Matcher matcher = POD_VERSION.matcher(path.toString());
            if (matcher.matches()) {
                return matcher.group(1);
            } else {
                log.error("POD Env JAR file \"{}\" does not contain a version", path);
                return null;
            }
        } catch (Exception e) {
            Map<String, String> env = new HashMap<>();
            env.put("create", "true");
            try {
                FileSystem zipfs = FileSystems.newFileSystem(location.toURI(), env);
            } catch (Exception e1) {
                log.error("linux env create new FS error: {}", e1.getMessage());
            }
            return getVersionFromPod(location);
        }
    }

    private String getVersion(URL location) {
        try {
            log.info("location.toURI().getScheme(): {}", location.toURI().getScheme());
            Path path = Paths.get(location.toURI());
            log.info("path is :{}", path);
            return Files.isRegularFile(path) ? getVersionFromJar(path) : getVersionFromPom(path, "pom.xml");
        } catch (Exception ex) {
            Map<String, String> env = new HashMap<>();
            env.put("create", "true");
            try {
                FileSystem zipfs = FileSystems.newFileSystem(location.toURI(), env);
            } catch (Exception e) {
                log.error("linux env create new FS error: {}", e.getMessage());
            }
            return getVersion(location);
        }
    }

    /**
     * Gets the version for a JAR file.
     *
     * @param path Path to JAR file
     * @return Found version or {@code null}
     */
    private static String getVersionFromJar(final Path path) {
        log.info("getVersionFromJar path is : {}", path);
        if (path.toString().contains("SNAPSHOT") || path.toString().contains("snapshot")) {
            String jarFile = path.toString().substring(path.toString().lastIndexOf(File.separator),
                    path.toString().length());
            Matcher matcher = JAR_SNAPSHOT_VERSION.matcher(jarFile);
            if (matcher.find()) {
                return matcher.group();
            } else {
                log.error("JAR file \"{}\" does not contain a version", path);
                return null;
            }
        } else {
            Matcher matcher = JAR_RELEASE_VERSION.matcher(path.toString());
            if (matcher.matches()) {
                return matcher.group(1);
            } else {
                log.error("JAR file \"{}\" does not contain a version", path);
                return null;
            }
        }
    }

    /**
     * Gets the version for a folder by searching for a pom.xml in the folder and its folders.
     *
     * @param path Path to a folder
     * @return Found version or {@code null}
     */
    private static String getVersionFromPom(Path path, String fileName) {
        log.info("getVersionFromPom path is : {}", path);
        Path actualFile = getFile(path, fileName);
        if (actualFile == null)
            return null;

        String content;
        try {
            content = new String(Files.readAllBytes(actualFile), StandardCharsets.UTF_8);
        } catch (IOException ex) {
            log.error("Failed reading \"{}\"", path);
            return null;
        }

        Matcher matcher = POM_VERSION.matcher(content);
        String version = "";
        if (matcher.find()) {
            version = matcher.group(1);
        } else {
            log.warn("POM \"{}\" does not contain a version", actualFile);
        }
        if (version.contains("${") || !StringUtils.hasText(version)) {
            Path buildFile = getFile(path, "maven-archiver/pom.properties");
            if (buildFile == null) {
                return null;
            }
            Properties properties = new Properties();
            try {
                properties.load(Files.newInputStream(buildFile));
                version = properties.getProperty("version");
            } catch (Exception e) {
                return version;
            }
        }
        return version;
    }

    @Nullable
    private static Path getFile(Path path, String fileName) {
        Path file;

        while (true) {
            file = path.resolve(fileName);
            if (Files.exists(file)) {
                break;
            }

            path = path.getParent();
            if (path == null) {
                log.warn("pom.xml is missing for \"{}\"", path);
                return null;
            }
        }
        return file;
    }

    @Bean
    public RequestInterceptor workflowRequestInterceptor(String serviceVersion) {
        return new WorkflowRequestInterceptor(serviceVersion);
    }

}
