//! 证书生成与管理 (rcgen 0.11)

use once_cell::sync::OnceCell;
use rcgen::{Certificate, CertificateParams, DistinguishedName, DnType, SanType};
use std::path::PathBuf;
use std::sync::Arc;

use crate::utils::AppError;

/// 证书和私钥
pub struct CertificateBundle {
    /// PEM 格式的证书
    pub cert_pem: String,
    /// PEM 格式的私钥
    pub key_pem: String,
}

/// 全局证书存储
static CERTIFICATE: OnceCell<Arc<CertificateBundle>> = OnceCell::new();

impl CertificateBundle {
    /// 生成新的自签名证书
    pub fn generate(device_id: &str, ip_addresses: Vec<String>) -> Result<Self, AppError> {
        // 配置证书参数
        let mut params = CertificateParams::default();

        // 设置证书主题
        let mut dn = DistinguishedName::new();
        dn.push(DnType::CommonName, format!("FlashSend-{}", &device_id[..8.min(device_id.len())]));
        dn.push(DnType::OrganizationName, "Flash Send");
        params.distinguished_name = dn;

        // 设置 SAN (Subject Alternative Names)
        let mut san_list = vec![
            SanType::DnsName("localhost".to_string()),
        ];
        
        // 添加 IP 地址
        for ip in ip_addresses {
            if let Ok(ip_addr) = ip.parse() {
                san_list.push(SanType::IpAddress(ip_addr));
            }
        }
        
        // 添加本地回环地址
        san_list.push(SanType::IpAddress("127.0.0.1".parse().unwrap()));
        params.subject_alt_names = san_list;

        // 生成证书
        let cert = Certificate::from_params(params)
            .map_err(|e| AppError::Tls(format!("Failed to generate certificate: {}", e)))?;

        Ok(Self {
            cert_pem: cert.serialize_pem()
                .map_err(|e| AppError::Tls(format!("Failed to serialize cert: {}", e)))?,
            key_pem: cert.serialize_private_key_pem(),
        })
    }

    /// 从文件加载证书
    pub fn load_from_files(cert_path: &PathBuf, key_path: &PathBuf) -> Result<Self, AppError> {
        let cert_pem = std::fs::read_to_string(cert_path)?;
        let key_pem = std::fs::read_to_string(key_path)?;
        Ok(Self { cert_pem, key_pem })
    }

    /// 保存证书到文件
    pub fn save_to_files(&self, cert_path: &PathBuf, key_path: &PathBuf) -> Result<(), AppError> {
        std::fs::write(cert_path, &self.cert_pem)?;
        std::fs::write(key_path, &self.key_pem)?;
        log::info!("Certificate saved to {:?}", cert_path);
        Ok(())
    }
}

/// 证书管理器
pub struct CertificateManager;

impl CertificateManager {
    /// 初始化证书 (加载已有或生成新证书)
    pub fn init(
        app_data_dir: &PathBuf,
        device_id: &str,
        ip_addresses: Vec<String>,
    ) -> Result<Arc<CertificateBundle>, AppError> {
        let cert_dir = app_data_dir.join("certs");
        std::fs::create_dir_all(&cert_dir)?;

        let cert_path = cert_dir.join("cert.pem");
        let key_path = cert_dir.join("key.pem");

        let bundle = if cert_path.exists() && key_path.exists() {
            log::info!("Loading existing certificate");
            CertificateBundle::load_from_files(&cert_path, &key_path)?
        } else {
            log::info!("Generating new self-signed certificate");
            let bundle = CertificateBundle::generate(device_id, ip_addresses)?;
            bundle.save_to_files(&cert_path, &key_path)?;
            bundle
        };

        let arc_bundle = Arc::new(bundle);
        CERTIFICATE
            .set(arc_bundle.clone())
            .map_err(|_| AppError::Tls("Certificate already initialized".to_string()))?;

        Ok(arc_bundle)
    }

    /// 获取当前证书
    pub fn get() -> Option<Arc<CertificateBundle>> {
        CERTIFICATE.get().cloned()
    }

    /// 强制重新生成证书
    pub fn regenerate(
        app_data_dir: &PathBuf,
        device_id: &str,
        ip_addresses: Vec<String>,
    ) -> Result<CertificateBundle, AppError> {
        let cert_dir = app_data_dir.join("certs");
        std::fs::create_dir_all(&cert_dir)?;

        let cert_path = cert_dir.join("cert.pem");
        let key_path = cert_dir.join("key.pem");

        let bundle = CertificateBundle::generate(device_id, ip_addresses)?;
        bundle.save_to_files(&cert_path, &key_path)?;

        Ok(bundle)
    }
}
