//! SSL/TLS Configuration
//!
//! Manages SSL certificates for HTTPS support, including
//! Let's Encrypt automatic certificate provisioning.

use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// SSL configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SslConfig {
    /// Enable HTTPS
    pub enabled: bool,
    /// Certificate file path
    pub cert_path: Option<PathBuf>,
    /// Private key file path
    pub key_path: Option<PathBuf>,
    /// Enable Let's Encrypt auto-provisioning
    pub auto_provision: bool,
    /// Let's Encrypt account email
    pub acme_email: Option<String>,
    /// Use Let's Encrypt staging (for testing)
    pub acme_staging: bool,
    /// Certificate cache directory
    pub cache_dir: PathBuf,
}

impl Default for SslConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            cert_path: None,
            key_path: None,
            auto_provision: false,
            acme_email: None,
            acme_staging: false,
            cache_dir: PathBuf::from("/var/cache/synor-hosting/certs"),
        }
    }
}

impl SslConfig {
    /// Create SSL config for production with Let's Encrypt
    pub fn letsencrypt(email: String) -> Self {
        Self {
            enabled: true,
            cert_path: None,
            key_path: None,
            auto_provision: true,
            acme_email: Some(email),
            acme_staging: false,
            cache_dir: PathBuf::from("/var/cache/synor-hosting/certs"),
        }
    }

    /// Create SSL config with manual certificates
    pub fn manual(cert_path: PathBuf, key_path: PathBuf) -> Self {
        Self {
            enabled: true,
            cert_path: Some(cert_path),
            key_path: Some(key_path),
            auto_provision: false,
            acme_email: None,
            acme_staging: false,
            cache_dir: PathBuf::from("/var/cache/synor-hosting/certs"),
        }
    }

    /// Validate the SSL configuration
    pub fn validate(&self) -> Result<()> {
        if !self.enabled {
            return Ok(());
        }

        if self.auto_provision {
            if self.acme_email.is_none() {
                return Err(Error::Config(
                    "ACME email required for auto-provisioning".into(),
                ));
            }
        } else {
            // Manual certs required
            if self.cert_path.is_none() || self.key_path.is_none() {
                return Err(Error::Config(
                    "Certificate and key paths required when not using auto-provisioning".into(),
                ));
            }

            // Check files exist
            if let Some(cert) = &self.cert_path {
                if !cert.exists() {
                    return Err(Error::Config(format!(
                        "Certificate file not found: {}",
                        cert.display()
                    )));
                }
            }

            if let Some(key) = &self.key_path {
                if !key.exists() {
                    return Err(Error::Config(format!(
                        "Key file not found: {}",
                        key.display()
                    )));
                }
            }
        }

        Ok(())
    }
}

/// Certificate manager for automatic provisioning
pub struct CertificateManager {
    config: SslConfig,
}

impl CertificateManager {
    /// Create a new certificate manager
    pub fn new(config: SslConfig) -> Self {
        Self { config }
    }

    /// Get or provision certificate for a domain
    pub async fn get_certificate(&self, domain: &str) -> Result<Certificate> {
        // Check cache first
        if let Some(cert) = self.load_cached_cert(domain)? {
            if !cert.is_expired() {
                return Ok(cert);
            }
        }

        // Provision new certificate
        if self.config.auto_provision {
            self.provision_certificate(domain).await
        } else {
            Err(Error::Config(format!(
                "No certificate for domain {} and auto-provisioning is disabled",
                domain
            )))
        }
    }

    /// Load cached certificate
    fn load_cached_cert(&self, domain: &str) -> Result<Option<Certificate>> {
        let cert_path = self.config.cache_dir.join(format!("{}.crt", domain));
        let key_path = self.config.cache_dir.join(format!("{}.key", domain));

        if cert_path.exists() && key_path.exists() {
            let cert_pem = std::fs::read_to_string(&cert_path)?;
            let key_pem = std::fs::read_to_string(&key_path)?;

            Ok(Some(Certificate {
                domain: domain.to_string(),
                cert_pem,
                key_pem,
                not_after: 0, // TODO: Parse from cert
            }))
        } else {
            Ok(None)
        }
    }

    /// Provision a new certificate from Let's Encrypt
    async fn provision_certificate(&self, domain: &str) -> Result<Certificate> {
        // This is a stub - real implementation would use ACME protocol
        // with something like the `acme` crate

        eprintln!(
            "Would provision certificate for {} (staging: {})",
            domain, self.config.acme_staging
        );

        Err(Error::Config(
            "Certificate auto-provisioning not yet implemented".into(),
        ))
    }

    /// Save certificate to cache
    fn save_cert(&self, cert: &Certificate) -> Result<()> {
        std::fs::create_dir_all(&self.config.cache_dir)?;

        let cert_path = self.config.cache_dir.join(format!("{}.crt", cert.domain));
        let key_path = self.config.cache_dir.join(format!("{}.key", cert.domain));

        std::fs::write(&cert_path, &cert.cert_pem)?;
        std::fs::write(&key_path, &cert.key_pem)?;

        Ok(())
    }
}

/// SSL Certificate
#[derive(Debug, Clone)]
pub struct Certificate {
    /// Domain name
    pub domain: String,
    /// Certificate PEM
    pub cert_pem: String,
    /// Private key PEM
    pub key_pem: String,
    /// Expiration timestamp (Unix)
    pub not_after: u64,
}

impl Certificate {
    /// Check if certificate is expired
    pub fn is_expired(&self) -> bool {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Consider expired if less than 30 days remaining
        self.not_after < now + 30 * 24 * 60 * 60
    }

    /// Get days until expiration
    pub fn days_until_expiry(&self) -> i64 {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        ((self.not_after as i64) - (now as i64)) / (24 * 60 * 60)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_config() {
        let config = SslConfig::default();
        assert!(!config.enabled);
        assert!(!config.auto_provision);
    }

    #[test]
    fn test_letsencrypt_config() {
        let config = SslConfig::letsencrypt("admin@example.com".to_string());
        assert!(config.enabled);
        assert!(config.auto_provision);
        assert_eq!(config.acme_email, Some("admin@example.com".to_string()));
    }

    #[test]
    fn test_validate_disabled() {
        let config = SslConfig::default();
        assert!(config.validate().is_ok());
    }

    #[test]
    fn test_validate_auto_requires_email() {
        let config = SslConfig {
            enabled: true,
            auto_provision: true,
            acme_email: None,
            ..Default::default()
        };
        assert!(config.validate().is_err());
    }

    #[test]
    fn test_certificate_expiry() {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        // Expired cert (7 days ago)
        let expired = Certificate {
            domain: "test.com".to_string(),
            cert_pem: String::new(),
            key_pem: String::new(),
            not_after: now - 7 * 24 * 60 * 60,
        };
        assert!(expired.is_expired());
        assert!(expired.days_until_expiry() < 0);

        // Valid cert (60 days)
        let valid = Certificate {
            domain: "test.com".to_string(),
            cert_pem: String::new(),
            key_pem: String::new(),
            not_after: now + 60 * 24 * 60 * 60,
        };
        assert!(!valid.is_expired());
        assert!(valid.days_until_expiry() > 50);
    }
}
