package com.gxwebsoft.common.core.utils;

import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.config.CertificateProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * 证书加载工具类
 * 支持多种证书加载方式，适配Docker容器化部署
 *
 * @author 科技小王子
 * @since 2025-01-26
 */
@Slf4j
@Component
public class CertificateLoader {

    private final CertificateProperties certConfig;

    public CertificateLoader(CertificateProperties certConfig) {
        this.certConfig = certConfig;
    }

    @PostConstruct
    public void init() {
        log.info("证书加载器初始化，加载模式：{}", certConfig.getLoadMode());
        if (certConfig.getLoadMode() == CertificateProperties.LoadMode.VOLUME) {
            log.info("Docker挂载卷证书路径：{}", certConfig.getCertRootPath());
            validateCertDirectory();
        }
    }

    /**
     * 验证证书目录是否存在
     */
    private void validateCertDirectory() {
        File certDir = new File(certConfig.getCertRootPath());
        if (!certDir.exists()) {
            log.warn("证书目录不存在：{}，将尝试创建", certConfig.getCertRootPath());
            if (!certDir.mkdirs()) {
                log.error("无法创建证书目录：{}", certConfig.getCertRootPath());
            }
        } else {
            log.info("证书目录验证成功：{}", certConfig.getCertRootPath());
        }
    }

    /**
     * 加载证书文件路径
     *
     * @param certPath 证书路径（可能是相对路径、绝对路径或classpath路径）
     * @return 实际的证书文件路径
     */
    public String loadCertificatePath(String certPath) {
        if (!StringUtils.hasText(certPath)) {
            throw new IllegalArgumentException("证书路径不能为空");
        }

        try {
            switch (certConfig.getLoadMode()) {
                case CLASSPATH:
                    return loadFromClasspath(certPath);
                case VOLUME:
                    return loadFromVolume(certPath);
                case FILESYSTEM:
                default:
                    return loadFromFileSystem(certPath);
            }
        } catch (Exception e) {
            log.error("加载证书失败，路径：{}", certPath, e);
            throw new RuntimeException("证书加载失败：" + certPath, e);
        }
    }

    /**
     * 从classpath加载证书
     */
    private String loadFromClasspath(String certPath) throws IOException {
        String resourcePath = certPath.startsWith("classpath:") ?
            certPath.substring("classpath:".length()) : certPath;

        ClassPathResource resource = new ClassPathResource(resourcePath);
        if (!resource.exists()) {
            throw new IOException("Classpath中找不到证书文件：" + resourcePath);
        }

        // 将classpath中的文件复制到临时目录
        Path tempFile = Files.createTempFile("cert_", ".pem");
        try (InputStream inputStream = resource.getInputStream()) {
            Files.copy(inputStream, tempFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
        }

        String tempPath = tempFile.toAbsolutePath().toString();
        log.debug("从classpath加载证书：{} -> {}", resourcePath, tempPath);
        return tempPath;
    }

    /**
     * 从Docker挂载卷加载证书
     */
    private String loadFromVolume(String certPath) {
        log.debug("尝试从Docker挂载卷加载证书：{}", certPath);

        // 如果是完整路径，直接使用
        if (certPath.startsWith("/") || certPath.contains(":")) {
            File file = new File(certPath);
            log.debug("检查完整路径文件是否存在：{}", certPath);
            if (file.exists()) {
                log.debug("使用完整路径加载证书：{}", certPath);
                return certPath;
            } else {
                log.error("完整路径文件不存在：{}", certPath);
            }
        }

        // 否则拼接挂载卷路径
        String fullPath = Paths.get(certConfig.getCertRootPath(), certPath).toString();
        File file = new File(fullPath);
        if (!file.exists()) {
            throw new RuntimeException("Docker挂载卷中找不到证书文件：" + fullPath);
        }

        log.debug("从Docker挂载卷加载证书：{}", fullPath);
        return fullPath;
    }

    /**
     * 从文件系统加载证书
     */
    private String loadFromFileSystem(String certPath) {
        File file = new File(certPath);
        if (!file.exists()) {
            throw new RuntimeException("文件系统中找不到证书文件：" + certPath);
        }

        log.debug("从文件系统加载证书：{}", certPath);
        return certPath;
    }

    /**
     * 检查证书文件是否存在
     *
     * @param certPath 证书路径
     * @return 是否存在
     */
    public boolean certificateExists(String certPath) {
        try {
            switch (certConfig.getLoadMode()) {
                case CLASSPATH:
                    String resourcePath = certPath.startsWith("classpath:") ?
                        certPath.substring("classpath:".length()) : certPath;
                    ClassPathResource resource = new ClassPathResource(resourcePath);
                    return resource.exists();
                case VOLUME:
                    String fullPath = certPath.startsWith("/") ? certPath :
                        Paths.get(certConfig.getCertRootPath(), certPath).toString();
                    return new File(fullPath).exists();
                case FILESYSTEM:
                default:
                    return new File(certPath).exists();
            }
        } catch (Exception e) {
            log.warn("检查证书文件存在性时出错：{}", certPath, e);
            return false;
        }
    }

    /**
     * 获取证书文件的输入流
     *
     * @param certPath 证书路径
     * @return 输入流
     */
    public InputStream getCertificateInputStream(String certPath) throws IOException {
        switch (certConfig.getLoadMode()) {
            case CLASSPATH:
                String resourcePath = certPath.startsWith("classpath:") ?
                    certPath.substring("classpath:".length()) : certPath;
                ClassPathResource resource = new ClassPathResource(resourcePath);
                return resource.getInputStream();
            case VOLUME:
            case FILESYSTEM:
            default:
                String actualPath = loadCertificatePath(certPath);
                return Files.newInputStream(Paths.get(actualPath));
        }
    }

    /**
     * 列出证书目录中的所有文件
     *
     * @return 证书文件列表
     */
    public String[] listCertificateFiles() {
        try {
            switch (certConfig.getLoadMode()) {
                case VOLUME:
                    File certDir = new File(certConfig.getCertRootPath());
                    if (certDir.exists() && certDir.isDirectory()) {
                        return certDir.list();
                    }
                    break;
                case CLASSPATH:
                    // classpath模式下不支持列出文件
                    log.warn("Classpath模式下不支持列出证书文件");
                    break;
                case FILESYSTEM:
                default:
                    // 文件系统模式下证书可能分散在不同目录，不支持统一列出
                    log.warn("文件系统模式下不支持列出证书文件");
                    break;
            }
        } catch (Exception e) {
            log.error("列出证书文件时出错", e);
        }
        return new String[0];
    }
}
