//! TLS configuration for Trading Service with mutual TLS
//!
//! This module provides enterprise-grade TLS configuration for the trading service:
//! - Mutual TLS (mTLS) for all gRPC connections
//! - Static certificate management via config crate
//! - Client certificate validation and authentication
//! - Performance optimized for HFT requirements

use anyhow::{Context, Result};
use config::manager::ConfigManager;

// Re-export shared TLS types from common for backward compatibility
pub use common::tls::{ClientIdentity, TlsProtocolVersion, UserRole};
use config::structures::TlsConfig;
use std::sync::Arc;
// TLS imports - TLS feature should be enabled in Cargo.toml
use tonic::transport::{Certificate, Identity, ServerTlsConfig};
use tracing::info;

use x509_parser::certificate::X509Certificate;
use x509_parser::extensions::{GeneralName, ParsedExtension};
use x509_parser::prelude::*;

/// TLS configuration for the trading service
#[derive(Debug, Clone)]
pub struct TradingServiceTlsConfig {
    /// Server certificate and private key
    pub server_identity: Identity,
    /// CA certificate for client verification
    pub ca_certificate: Certificate,
    /// Require client certificates
    pub require_client_cert: bool,
    /// TLS protocol version (1.2 or 1.3)
    pub protocol_version: TlsProtocolVersion,
    /// Certificate revocation checking enabled
    pub enable_revocation_check: bool,
    /// CRL distribution point URL (optional)
    pub crl_url: Option<String>,
}

impl TradingServiceTlsConfig {
    /// Create TLS configuration from certificate files
    pub async fn from_files(
        cert_path: &str,
        key_path: &str,
        ca_cert_path: &str,
        require_client_cert: bool,
    ) -> Result<Self> {
        info!("Loading TLS certificates from filesystem");

        // Read server certificate and key
        let cert_pem = tokio::fs::read_to_string(cert_path)
            .await
            .with_context(|| format!("Failed to read certificate file: {}", cert_path))?;

        let key_pem = tokio::fs::read_to_string(key_path)
            .await
            .with_context(|| format!("Failed to read private key file: {}", key_path))?;

        // Combine certificate and key for server identity
        let server_identity = Identity::from_pem(cert_pem, key_pem);

        // Read CA certificate for client verification
        let ca_pem = tokio::fs::read_to_string(ca_cert_path)
            .await
            .with_context(|| format!("Failed to read CA certificate file: {}", ca_cert_path))?;

        let ca_certificate = Certificate::from_pem(ca_pem);

        info!(
            "TLS certificates loaded successfully - mTLS: {}",
            require_client_cert
        );

        Ok(Self {
            server_identity,
            ca_certificate,
            require_client_cert,
            protocol_version: TlsProtocolVersion::Tls13,
            enable_revocation_check: false, // Default disabled for compatibility
            crl_url: None,
        })
    }

    /// Create TLS configuration from config crate
    pub async fn from_config(config_manager: &ConfigManager) -> Result<Self> {
        info!("Loading TLS certificates from configuration");

        // Get the service config which contains settings as JSON
        let service_config = config_manager.get_config();

        // Extract TLS config from the settings JSON field
        let tls_config: TlsConfig = serde_json::from_value(
            service_config
                .settings
                .get("tls")
                .cloned()
                .unwrap_or(serde_json::json!({})),
        )
        .unwrap_or_default();

        // Use environment variable for CA cert path with fallback to /app/certs
        let ca_cert_path = std::env::var("TLS_CA_PATH")
            .unwrap_or_else(|_| "/app/certs/trading_service/ca.crt".to_string());

        Self::from_files(
            &tls_config.cert_path,
            &tls_config.key_path,
            tls_config.ca_cert_path.as_deref().unwrap_or(&ca_cert_path),
            true, // Always require mTLS
        )
        .await
    }

    /// Convert to tonic ServerTlsConfig
    pub fn to_server_tls_config(&self) -> ServerTlsConfig {
        let mut tls_config = ServerTlsConfig::new().identity(self.server_identity.clone());

        if self.require_client_cert {
            tls_config = tls_config.client_ca_root(self.ca_certificate.clone());
        }

        tls_config
    }

    /// Validate client certificate and extract identity
    pub async fn validate_client_certificate(&self, cert_chain: &[u8]) -> Result<ClientIdentity> {
        // Parse the X.509 certificate from PEM format
        let (_, pem) = x509_parser::pem::parse_x509_pem(cert_chain)
            .map_err(|e| anyhow::anyhow!("Failed to parse PEM certificate: {}", e))?;

        let cert = pem
            .parse_x509()
            .map_err(|e| anyhow::anyhow!("Failed to parse X.509 certificate: {}", e))?;

        // Comprehensive certificate validation
        let client_identity = self.extract_and_validate_certificate(&cert).await?;

        tracing::info!(
            "Client certificate validated: CN={}, OU={}",
            client_identity.common_name,
            client_identity.organizational_unit
        );

        Ok(client_identity)
    }

    /// Extract and validate certificate with comprehensive security checks
    async fn extract_and_validate_certificate(
        &self,
        cert: &X509Certificate<'_>,
    ) -> Result<ClientIdentity> {
        // SECURITY CHECK 1: Certificate Validity Period (Expiration)
        self.validate_certificate_expiration(cert)?;

        // SECURITY CHECK 2: Certificate Purpose (Extended Key Usage)
        self.validate_certificate_purpose(cert)?;

        // SECURITY CHECK 3: Certificate Chain of Trust (Basic Constraints)
        self.validate_certificate_constraints(cert)?;

        // SECURITY CHECK 4: Critical Extensions Validation
        self.validate_critical_extensions(cert)?;

        // SECURITY CHECK 5: Subject Alternative Names (if present)
        self.validate_subject_alternative_names(cert)?;

        // SECURITY CHECK 6: Certificate Revocation Status (CRL/OCSP)
        if self.enable_revocation_check {
            self.check_revocation_status(cert).await?;
        }

        // Extract identity information from Subject DN
        let subject = cert.subject();

        // Extract Common Name (CN)
        let common_name = subject
            .iter_common_name()
            .next()
            .and_then(|cn| cn.as_str().ok())
            .ok_or_else(|| anyhow::anyhow!("Certificate missing Common Name (CN)"))?
            .to_string();

        // Extract Organizational Unit (OU) - required for RBAC
        let organizational_unit = subject
            .iter_organizational_unit()
            .next()
            .and_then(|ou| ou.as_str().ok())
            .ok_or_else(|| anyhow::anyhow!("Certificate missing Organizational Unit (OU)"))?
            .to_string();

        // Extract Serial Number
        let serial_number = format!("{:X}", cert.serial);

        // Extract Issuer CN
        let issuer = cert
            .issuer()
            .iter_common_name()
            .next()
            .and_then(|cn| cn.as_str().ok())
            .unwrap_or("Unknown Issuer")
            .to_string();

        // SECURITY: Validate organizational unit is in allowed list
        let allowed_ous = ["trading", "admin", "analytics", "risk", "compliance"];
        if !allowed_ous.contains(&organizational_unit.as_str()) {
            return Err(anyhow::anyhow!(
                "Organizational Unit '{}' is not authorized for access. Allowed: {:?}",
                organizational_unit,
                allowed_ous
            ));
        }

        // SECURITY: Validate common name format (prevent injection attacks)
        if !common_name
            .chars()
            .all(|c| c.is_alphanumeric() || c == '.' || c == '-' || c == '_')
        {
            return Err(anyhow::anyhow!(
                "Common Name contains invalid characters: {}",
                common_name
            ));
        }

        Ok(ClientIdentity {
            common_name,
            organizational_unit,
            serial_number,
            issuer,
        })
    }

    /// SECURITY CHECK 1: Validate certificate expiration
    fn validate_certificate_expiration(&self, cert: &X509Certificate<'_>) -> Result<()> {
        let validity = cert.validity();

        // Get current time
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map_err(|e| anyhow::anyhow!("System time error: {}", e))?
            .as_secs() as i64;

        // Check not before
        let not_before = validity.not_before.timestamp();
        if now < not_before {
            return Err(anyhow::anyhow!(
                "Certificate not yet valid. Valid from: {}",
                validity.not_before
            ));
        }

        // Check not after
        let not_after = validity.not_after.timestamp();
        if now > not_after {
            return Err(anyhow::anyhow!(
                "Certificate expired. Expired on: {}",
                validity.not_after
            ));
        }

        // SECURITY: Warn if certificate expires soon (within 30 days)
        let thirty_days_secs = 30 * 24 * 3600;
        if not_after - now < thirty_days_secs {
            let days_remaining = (not_after - now) / (24 * 3600);
            tracing::warn!(
                "Certificate expires soon! Days remaining: {}. Expiration: {}",
                days_remaining,
                validity.not_after
            );
        }

        Ok(())
    }

    /// SECURITY CHECK 2: Validate certificate purpose via Extended Key Usage
    fn validate_certificate_purpose(&self, cert: &X509Certificate<'_>) -> Result<()> {
        // Look for Extended Key Usage extension
        let mut has_client_auth = false;
        let mut has_eku_extension = false;

        for ext in cert.extensions() {
            if let ParsedExtension::ExtendedKeyUsage(eku) = ext.parsed_extension() {
                has_eku_extension = true;

                // Check for TLS Client Authentication (OID: 1.3.6.1.5.5.7.3.2)
                has_client_auth = eku.client_auth;

                if has_client_auth {
                    tracing::debug!("Certificate has TLS Client Authentication purpose");
                } else {
                    tracing::warn!(
                        "Certificate Extended Key Usage present but missing Client Auth. Purposes: {:?}",
                        eku
                    );
                }
            }
        }

        // SECURITY: Require Extended Key Usage with Client Auth for mTLS
        if has_eku_extension && !has_client_auth {
            return Err(anyhow::anyhow!(
                "Certificate does not have TLS Client Authentication purpose (Extended Key Usage)"
            ));
        }

        // If no EKU extension, we allow it (some CAs don't set this for client certs)
        // but log a warning for security awareness
        if !has_eku_extension {
            tracing::warn!(
                "Certificate missing Extended Key Usage extension - certificate purpose cannot be verified"
            );
        }

        Ok(())
    }

    /// SECURITY CHECK 3: Validate Basic Constraints (ensure not a CA certificate)
    fn validate_certificate_constraints(&self, cert: &X509Certificate<'_>) -> Result<()> {
        for ext in cert.extensions() {
            if let ParsedExtension::BasicConstraints(bc) = ext.parsed_extension() {
                // SECURITY: Client certificates should NOT be CA certificates
                if bc.ca {
                    return Err(anyhow::anyhow!(
                        "Client certificate has CA flag set - this is a CA certificate, not a client certificate"
                    ));
                }

                tracing::debug!("Certificate Basic Constraints validated: ca={}", bc.ca);
            }
        }

        Ok(())
    }

    /// SECURITY CHECK 4: Validate all critical extensions are recognized
    fn validate_critical_extensions(&self, cert: &X509Certificate<'_>) -> Result<()> {
        // List of recognized critical extensions (OIDs)
        let recognized_critical = [
            "2.5.29.15", // Key Usage
            "2.5.29.19", // Basic Constraints
            "2.5.29.37", // Extended Key Usage
            "2.5.29.17", // Subject Alternative Name
            "2.5.29.32", // Certificate Policies
            "2.5.29.35", // Authority Key Identifier
            "2.5.29.14", // Subject Key Identifier
        ];

        for ext in cert.extensions() {
            if ext.critical {
                let oid_str = ext.oid.to_id_string();

                // Check if this critical extension is recognized
                if !recognized_critical.contains(&oid_str.as_str()) {
                    return Err(anyhow::anyhow!(
                        "Certificate contains unrecognized critical extension: {} - cannot safely process",
                        oid_str
                    ));
                }

                tracing::debug!("Recognized critical extension: {}", oid_str);
            }
        }

        Ok(())
    }

    /// SECURITY CHECK 5: Validate Subject Alternative Names (if present)
    fn validate_subject_alternative_names(&self, cert: &X509Certificate<'_>) -> Result<()> {
        for ext in cert.extensions() {
            if let ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() {
                // Extract and validate SAN entries
                let mut san_entries = Vec::new();

                for name in &san.general_names {
                    match name {
                        GeneralName::DNSName(dns) => {
                            san_entries.push(format!("DNS:{}", dns));

                            // SECURITY: Validate DNS name format
                            if !Self::is_valid_dns_name(dns) {
                                return Err(anyhow::anyhow!(
                                    "Invalid DNS name in Subject Alternative Name: {}",
                                    dns
                                ));
                            }
                        },
                        GeneralName::RFC822Name(email) => {
                            san_entries.push(format!("Email:{}", email));
                        },
                        GeneralName::IPAddress(ip) => {
                            san_entries.push(format!("IP:{:?}", ip));
                        },
                        GeneralName::URI(uri) => {
                            san_entries.push(format!("URI:{}", uri));
                        },
                        _ => {
                            tracing::debug!("Other SAN type: {:?}", name);
                        },
                    }
                }

                if !san_entries.is_empty() {
                    tracing::debug!("Certificate Subject Alternative Names: {:?}", san_entries);
                }
            }
        }

        Ok(())
    }

    /// Validate DNS name format (prevent injection attacks)
    fn is_valid_dns_name(name: &str) -> bool {
        // DNS name validation: alphanumeric, dots, hyphens, underscores
        // Max 253 characters total, max 63 characters per label
        if name.is_empty() || name.len() > 253 {
            return false;
        }

        for label in name.split('.') {
            if label.is_empty() || label.len() > 63 {
                return false;
            }

            // Check valid characters: alphanumeric, hyphen, underscore
            // Cannot start or end with hyphen
            if !label
                .chars()
                .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
            {
                return false;
            }

            if label.starts_with('-') || label.ends_with('-') {
                return false;
            }
        }

        true
    }

    /// SECURITY CHECK 6: Check certificate revocation status via CRL or OCSP
    async fn check_revocation_status(&self, cert: &X509Certificate<'_>) -> Result<()> {
        // Check if certificate has CRL Distribution Points or OCSP extensions
        let mut crl_urls: Vec<String> = Vec::new();
        let ocsp_urls: Vec<String> = Vec::new();

        for ext in cert.extensions() {
            // Check for CRL Distribution Points (OID: 2.5.29.31)
            if ext.oid.to_id_string() == "2.5.29.31" {
                tracing::debug!("Certificate has CRL Distribution Points extension");

                // Add configured CRL URL if available
                if let Some(ref url) = self.crl_url {
                    crl_urls.push(url.clone());
                }
            }

            // Check for Authority Information Access (OID: 1.3.6.1.5.5.7.1.1) for OCSP
            if ext.oid.to_id_string() == "1.3.6.1.5.5.7.1.1" {
                tracing::debug!("Certificate has Authority Information Access extension (OCSP)");
                // OCSP URL extraction would go here
            }
        }

        // Perform CRL check if URLs are available
        if !crl_urls.is_empty() {
            for crl_url in &crl_urls {
                match self.check_crl_revocation(cert, crl_url).await {
                    Ok(is_revoked) => {
                        if is_revoked {
                            return Err(anyhow::anyhow!(
                                "Certificate has been revoked (CRL check against: {})",
                                crl_url
                            ));
                        }
                        tracing::info!("Certificate CRL check passed: {}", crl_url);
                        return Ok(()); // Successful check, certificate not revoked
                    },
                    Err(e) => {
                        tracing::warn!("CRL check failed for {}: {}", crl_url, e);
                        // Continue to next CRL URL or OCSP
                    },
                }
            }
        }

        // Perform OCSP check if URLs are available and CRL failed
        if !ocsp_urls.is_empty() {
            for ocsp_url in &ocsp_urls {
                match self.check_ocsp_revocation(cert, ocsp_url).await {
                    Ok(is_revoked) => {
                        if is_revoked {
                            return Err(anyhow::anyhow!(
                                "Certificate has been revoked (OCSP check against: {})",
                                ocsp_url
                            ));
                        }
                        tracing::info!("Certificate OCSP check passed: {}", ocsp_url);
                        return Ok(()); // Successful check, certificate not revoked
                    },
                    Err(e) => {
                        tracing::warn!("OCSP check failed for {}: {}", ocsp_url, e);
                    },
                }
            }
        }

        // If revocation checking is enabled but no methods succeeded
        if crl_urls.is_empty() && ocsp_urls.is_empty() {
            tracing::warn!(
                "Certificate revocation checking enabled but no CRL or OCSP URLs available"
            );
        }

        Ok(())
    }

    /// Check certificate against CRL (Certificate Revocation List)
    async fn check_crl_revocation(
        &self,
        cert: &X509Certificate<'_>,
        crl_url: &str,
    ) -> Result<bool> {
        tracing::debug!("Checking certificate revocation via CRL: {}", crl_url);

        // Download CRL from URL
        let client = reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(10))
            .build()
            .context("Failed to create HTTP client for CRL download")?;

        let crl_response = client
            .get(crl_url)
            .send()
            .await
            .context("Failed to download CRL")?;

        let crl_bytes = crl_response
            .bytes()
            .await
            .context("Failed to read CRL response")?;

        // Parse CRL
        let (_, crl) =
            x509_parser::revocation_list::CertificateRevocationList::from_der(&crl_bytes)
                .map_err(|e| anyhow::anyhow!("Failed to parse CRL: {}", e))?;

        // Check if certificate serial number is in revoked list
        for revoked_cert in crl.iter_revoked_certificates() {
            if revoked_cert.raw_serial() == cert.raw_serial() {
                tracing::error!(
                    "Certificate REVOKED! Serial: {:X}, Revocation date: {:?}",
                    cert.serial,
                    revoked_cert.revocation_date
                );
                return Ok(true); // Certificate is revoked
            }
        }

        Ok(false) // Certificate not found in CRL, not revoked
    }

    /// Check certificate via OCSP (Online Certificate Status Protocol)
    async fn check_ocsp_revocation(
        &self,
        _cert: &X509Certificate<'_>,
        ocsp_url: &str,
    ) -> Result<bool> {
        tracing::debug!("Checking certificate revocation via OCSP: {}", ocsp_url);

        // OCSP checking requires DER-encoded OCSPRequest per RFC 6960.
        // Full implementation needs: encode request, POST to responder, parse response,
        // verify responder signature. CRL checking (already implemented) serves as
        // the primary revocation mechanism.
        tracing::warn!(
            "OCSP checking not fully implemented — CRL-based revocation is active as primary mechanism. \
             OCSP responder: {}",
            ocsp_url
        );

        // Return false (not revoked) — CRL check handles revocation detection
        Ok(false)
    }
}

/// TLS interceptor for gRPC requests
#[derive(Clone)]
pub struct TlsInterceptor {
    tls_config: Arc<TradingServiceTlsConfig>,
}

impl TlsInterceptor {
    /// Create new TLS interceptor
    pub fn new(tls_config: Arc<TradingServiceTlsConfig>) -> Self {
        Self { tls_config }
    }

    /// Extract and validate client certificate from request
    pub async fn extract_client_identity<T>(
        &self,
        request: &tonic::Request<T>,
    ) -> Result<ClientIdentity> {
        // Get TLS info from request metadata
        let tls_info = request
            .extensions()
            .get::<tonic::transport::server::TlsConnectInfo<()>>()
            .ok_or_else(|| anyhow::anyhow!("No TLS connection info found"))?;

        // Extract client certificate if present
        if let Some(cert_der) = tls_info
            .peer_certs()
            .and_then(|certs| certs.first().cloned())
        {
            // Convert DER to PEM for processing
            let cert_pem = self.der_to_pem(&cert_der)?;
            self.tls_config.validate_client_certificate(&cert_pem).await
        } else {
            Err(anyhow::anyhow!("No client certificate provided"))
        }
    }

    /// Convert DER certificate to PEM format
    fn der_to_pem(&self, der_bytes: &[u8]) -> Result<Vec<u8>> {
        use base64::{engine::general_purpose, Engine as _};

        let b64_cert = general_purpose::STANDARD.encode(der_bytes);
        let pem_cert = format!(
            "-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----\n",
            b64_cert
                .chars()
                .collect::<Vec<char>>()
                .chunks(64)
                .map(|chunk| chunk.iter().collect::<String>())
                .collect::<Vec<String>>()
                .join("\n")
        );

        Ok(pem_cert.into_bytes())
    }
}

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

    #[test]
    fn test_client_identity_authorization() {
        let trading_identity = ClientIdentity {
            common_name: "trader1.trading.foxhunt.internal".to_string(),
            organizational_unit: "trading".to_string(),
            serial_number: "12345".to_string(),
            issuer: "Foxhunt Trading CA".to_string(),
        };

        assert!(trading_identity.is_authorized_for_trading());
        assert!(trading_identity.is_authorized_for_readonly());
        assert_eq!(trading_identity.get_role(), UserRole::Trader);

        let readonly_identity = ClientIdentity {
            common_name: "analyst1.analytics.foxhunt.internal".to_string(),
            organizational_unit: "analytics".to_string(),
            serial_number: "12346".to_string(),
            issuer: "Foxhunt Trading CA".to_string(),
        };

        assert!(!readonly_identity.is_authorized_for_trading());
        assert!(readonly_identity.is_authorized_for_readonly());
        assert_eq!(readonly_identity.get_role(), UserRole::Analyst);
    }

    #[test]
    fn test_user_role_permissions() {
        let trader = UserRole::Trader;
        let permissions = trader.get_permissions();

        assert!(permissions.contains(&"trading.submit_order"));
        assert!(permissions.contains(&"trading.cancel_order"));
        assert!(!permissions.contains(&"system.configure"));

        let readonly = UserRole::ReadOnly;
        let readonly_permissions = readonly.get_permissions();

        assert!(!readonly_permissions.contains(&"trading.submit_order"));
        assert!(readonly_permissions.contains(&"analytics.view_data"));
    }
}
