package com.gxwebsoft.common.core.service;

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.Service;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 证书管理服务
 * 负责处理不同环境下的证书加载、验证和管理
 *
 * @author 科技小王子
 * @since 2024-07-26
 */
@Slf4j
@Service
public class CertificateService {

    private final CertificateProperties certificateProperties;

    public CertificateService(CertificateProperties certificateProperties) {
        this.certificateProperties = certificateProperties;
    }

    @PostConstruct
    public void init() {
        log.info("证书服务初始化，当前加载模式: {}", certificateProperties.getLoadMode());
        log.info("证书根路径: {}", certificateProperties.getCertRootPath());

        // 检查证书目录和文件
        checkCertificateDirectories();
    }

    /**
     * 获取证书文件的输入流
     *
     * @param certType 证书类型（wechat/alipay）
     * @param fileName 文件名
     * @return 输入流
     * @throws IOException 文件读取异常
     */
    public InputStream getCertificateInputStream(String certType, String fileName) throws IOException {
        String certPath = certificateProperties.getCertificatePath(certType, fileName);

        if (certificateProperties.isClasspathMode()) {
            // 从classpath加载
            Resource resource = new ClassPathResource(certPath);
            if (!resource.exists()) {
                throw new IOException("证书文件不存在: " + certPath);
            }
            log.debug("从classpath加载证书: {}", certPath);
            return resource.getInputStream();
        } else {
            // 从文件系统加载
            File file = new File(certPath);
            if (!file.exists()) {
                throw new IOException("证书文件不存在: " + certPath);
            }
            log.debug("从文件系统加载证书: {}", certPath);
            return Files.newInputStream(file.toPath());
        }
    }

    /**
     * 获取证书文件路径
     *
     * @param certType 证书类型
     * @param fileName 文件名
     * @return 文件路径
     */
    public String getCertificateFilePath(String certType, String fileName) {
        return certificateProperties.getCertificatePath(certType, fileName);
    }

    /**
     * 检查证书文件是否存在
     *
     * @param certType 证书类型
     * @param fileName 文件名
     * @return 是否存在
     */
    public boolean certificateExists(String certType, String fileName) {
        try {
            String certPath = certificateProperties.getCertificatePath(certType, fileName);

            if (certificateProperties.isClasspathMode()) {
                Resource resource = new ClassPathResource(certPath);
                return resource.exists();
            } else {
                File file = new File(certPath);
                return file.exists() && file.isFile();
            }
        } catch (Exception e) {
            log.error("检查证书文件存在性时出错: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取微信支付证书路径
     *
     * @param fileName 文件名
     * @return 证书路径
     */
    public String getWechatPayCertPath(String fileName) {
        return certificateProperties.getWechatPayCertPath(fileName);
    }

    /**
     * 获取支付宝证书路径
     *
     * @param fileName 文件名
     * @return 证书路径
     */
    public String getAlipayCertPath(String fileName) {
        return certificateProperties.getAlipayCertPath(fileName);
    }

    /**
     * 验证X509证书
     *
     * @param certType 证书类型
     * @param fileName 文件名
     * @return 证书信息
     */
    public CertificateInfo validateX509Certificate(String certType, String fileName) {
        try (InputStream inputStream = getCertificateInputStream(certType, fileName)) {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);

            CertificateInfo info = new CertificateInfo();
            info.setSubject(cert.getSubjectX500Principal().toString());
            info.setIssuer(cert.getIssuerX500Principal().toString());
            info.setNotBefore(convertToLocalDateTime(cert.getNotBefore()));
            info.setNotAfter(convertToLocalDateTime(cert.getNotAfter()));
            info.setSerialNumber(cert.getSerialNumber().toString());
            info.setValid(isValidDate(cert.getNotBefore(), cert.getNotAfter()));

            return info;
        } catch (Exception e) {
            log.error("验证证书失败: {}/{}, 错误: {}", certType, fileName, e.getMessage());
            return null;
        }
    }

    /**
     * 检查证书目录结构
     */
    private void checkCertificateDirectories() {
        String[] certTypes = {"wechat", "alipay"};

        for (String certType : certTypes) {
            if (!certificateProperties.isClasspathMode()) {
                // 检查文件系统目录
                String dirPath = certificateProperties.getCertificatePath(certType, "");
                File dir = new File(dirPath);
                if (!dir.exists()) {
                    log.warn("证书目录不存在: {}", dirPath);
                } else {
                    log.info("证书目录存在: {}", dirPath);
                }
            }
        }
    }

    /**
     * 获取所有证书状态
     *
     * @return 证书状态映射
     */
    public Map<String, Object> getAllCertificateStatus() {
        Map<String, Object> status = new HashMap<>();

        // 微信支付证书状态
        Map<String, Object> wechatStatus = new HashMap<>();
        CertificateProperties.WechatPayConfig wechatConfig = certificateProperties.getWechatPay();
        wechatStatus.put("privateKey", getCertStatus("wechat", wechatConfig.getDev().getPrivateKeyFile()));
        wechatStatus.put("apiclientCert", getCertStatus("wechat", wechatConfig.getDev().getApiclientCertFile()));
        wechatStatus.put("wechatpayCert", getCertStatus("wechat", wechatConfig.getDev().getWechatpayCertFile()));
        status.put("wechat", wechatStatus);

        // 支付宝证书状态
        Map<String, Object> alipayStatus = new HashMap<>();
        CertificateProperties.AlipayConfig alipayConfig = certificateProperties.getAlipay();
        alipayStatus.put("appPrivateKey", getCertStatus("alipay", alipayConfig.getAppPrivateKeyFile()));
        alipayStatus.put("appCertPublicKey", getCertStatus("alipay", alipayConfig.getAppCertPublicKeyFile()));
        alipayStatus.put("alipayCertPublicKey", getCertStatus("alipay", alipayConfig.getAlipayCertPublicKeyFile()));
        alipayStatus.put("alipayRootCert", getCertStatus("alipay", alipayConfig.getAlipayRootCertFile()));
        status.put("alipay", alipayStatus);

        // 系统信息
        Map<String, Object> systemInfo = new HashMap<>();
        systemInfo.put("loadMode", certificateProperties.getLoadMode());
        systemInfo.put("certRootPath", certificateProperties.getCertRootPath());
        systemInfo.put("devCertPath", certificateProperties.getDevCertPath());
        status.put("system", systemInfo);

        return status;
    }

    /**
     * 获取单个证书状态
     */
    private Map<String, Object> getCertStatus(String certType, String fileName) {
        Map<String, Object> status = new HashMap<>();
        status.put("fileName", fileName);
        status.put("exists", certificateExists(certType, fileName));
        status.put("path", getCertificateFilePath(certType, fileName));

        // 如果是.crt或.pem文件，尝试验证证书
        if (fileName.endsWith(".crt") || fileName.endsWith(".pem")) {
            CertificateInfo certInfo = validateX509Certificate(certType, fileName);
            status.put("certificateInfo", certInfo);
        }

        return status;
    }

    /**
     * 检查日期是否有效
     */
    private boolean isValidDate(Date notBefore, Date notAfter) {
        Date now = new Date();
        return now.after(notBefore) && now.before(notAfter);
    }

    /**
     * 将Date转换为LocalDateTime
     */
    private LocalDateTime convertToLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * 证书信息类
     */
    public static class CertificateInfo {
        private String subject;
        private String issuer;
        private LocalDateTime notBefore;
        private LocalDateTime notAfter;
        private String serialNumber;
        private boolean valid;

        // Getters and Setters
        public String getSubject() { return subject; }
        public void setSubject(String subject) { this.subject = subject; }

        public String getIssuer() { return issuer; }
        public void setIssuer(String issuer) { this.issuer = issuer; }

        public LocalDateTime getNotBefore() { return notBefore; }
        public void setNotBefore(LocalDateTime notBefore) { this.notBefore = notBefore; }

        public LocalDateTime getNotAfter() { return notAfter; }
        public void setNotAfter(LocalDateTime notAfter) { this.notAfter = notAfter; }

        public String getSerialNumber() { return serialNumber; }
        public void setSerialNumber(String serialNumber) { this.serialNumber = serialNumber; }

        public boolean isValid() { return valid; }
        public void setValid(boolean valid) { this.valid = valid; }
    }
}
