"""
SSL certificate utilities for HTTPS support
"""

import os
import logging
import ipaddress
from pathlib import Path
from typing import Tuple, Optional
from datetime import datetime, timedelta

logger = logging.getLogger(__name__)

def generate_self_signed_certificate(cert_path: str, key_path: str, 
                                   common_name: str = "localhost") -> bool:
    """
    Generate a self-signed SSL certificate for HTTPS support
    
    Args:
        cert_path: Path where certificate file will be saved
        key_path: Path where private key file will be saved
        common_name: Common name for the certificate (usually hostname)
        
    Returns:
        True if certificate was generated successfully, False otherwise
    """
    try:
        from cryptography import x509
        from cryptography.x509.oid import NameOID
        from cryptography.hazmat.primitives import hashes, serialization
        from cryptography.hazmat.primitives.asymmetric import rsa
        
        logger.info(f"Generating self-signed SSL certificate for {common_name}")
        
        # Generate private key
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
        )
        
        # Create certificate subject and issuer (same for self-signed)
        subject = issuer = x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "CA"),
            x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, "MbetterClient"),
            x509.NameAttribute(NameOID.COMMON_NAME, common_name),
        ])
        
        # Create certificate
        cert = x509.CertificateBuilder().subject_name(
            subject
        ).issuer_name(
            issuer
        ).public_key(
            private_key.public_key()
        ).serial_number(
            x509.random_serial_number()
        ).not_valid_before(
            datetime.utcnow()
        ).not_valid_after(
            datetime.utcnow() + timedelta(days=365*10)  # Valid for 10 years
        ).add_extension(
            x509.SubjectAlternativeName([
                x509.DNSName(common_name),
                x509.DNSName("localhost"),
                x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
                x509.IPAddress(ipaddress.IPv6Address("::1")),  # IPv6 localhost
            ]),
            critical=False,
        ).sign(private_key, hashes.SHA256())
        
        # Ensure directories exist
        Path(cert_path).parent.mkdir(parents=True, exist_ok=True)
        Path(key_path).parent.mkdir(parents=True, exist_ok=True)
        
        # Write certificate
        with open(cert_path, "wb") as f:
            f.write(cert.public_bytes(serialization.Encoding.PEM))
        
        # Write private key
        with open(key_path, "wb") as f:
            f.write(private_key.private_bytes(
                encoding=serialization.Encoding.PEM,
                format=serialization.PrivateFormat.PKCS8,
                encryption_algorithm=serialization.NoEncryption()
            ))
        
        # Set appropriate permissions (read-only for owner)
        os.chmod(cert_path, 0o600)
        os.chmod(key_path, 0o600)
        
        logger.info(f"SSL certificate generated successfully:")
        logger.info(f"  Certificate: {cert_path}")
        logger.info(f"  Private key: {key_path}")
        logger.info(f"  Valid until: {datetime.utcnow() + timedelta(days=365*10)}")
        
        return True
        
    except ImportError as e:
        logger.error(f"Required cryptography library not available: {e}")
        logger.error("Please install it with: pip install cryptography")
        return False
    except Exception as e:
        logger.error(f"Failed to generate SSL certificate: {e}")
        return False


def get_ssl_certificate_paths(user_data_dir: Path, auto_generate: bool = True) -> Tuple[Optional[str], Optional[str]]:
    """
    Get SSL certificate and key file paths, generating them if needed
    
    Args:
        user_data_dir: User data directory path
        auto_generate: Whether to auto-generate certificate if it doesn't exist
        
    Returns:
        Tuple of (cert_path, key_path) or (None, None) if unavailable
    """
    try:
        ssl_dir = user_data_dir / "ssl"
        ssl_dir.mkdir(parents=True, exist_ok=True)
        
        cert_path = ssl_dir / "server.crt"
        key_path = ssl_dir / "server.key"
        
        # Check if certificate files exist and are valid
        if cert_path.exists() and key_path.exists():
            validation_result = _validate_certificate_files(str(cert_path), str(key_path))
            if validation_result == "valid":
                logger.info(f"Using existing SSL certificate: {cert_path}")
                return str(cert_path), str(key_path)
            elif validation_result == "expired":
                logger.warning("Existing SSL certificate has expired, regenerating...")
            else:
                logger.warning("Existing SSL certificate is invalid, will regenerate")
        
        # Generate new certificate if needed and requested
        if auto_generate:
            if generate_self_signed_certificate(str(cert_path), str(key_path)):
                return str(cert_path), str(key_path)
        
        return None, None
        
    except Exception as e:
        logger.error(f"Failed to get SSL certificate paths: {e}")
        return None, None


def _validate_certificate_files(cert_path: str, key_path: str) -> str:
    """
    Validate that certificate and key files exist and are readable
    
    Args:
        cert_path: Path to certificate file
        key_path: Path to private key file
        
    Returns:
        "valid" if files are valid and not expired
        "expired" if files are valid but certificate is expired
        "invalid" if files are invalid or unreadable
    """
    try:
        # Check if files exist and are readable
        if not os.path.isfile(cert_path) or not os.access(cert_path, os.R_OK):
            return "invalid"
        
        if not os.path.isfile(key_path) or not os.access(key_path, os.R_OK):
            return "invalid"
        
        # Try to load the certificate to verify it's valid
        try:
            from cryptography import x509
            from cryptography.hazmat.primitives import serialization
            
            # Load and parse certificate
            with open(cert_path, "rb") as f:
                cert = x509.load_pem_x509_certificate(f.read())
            
            # Load and parse private key
            with open(key_path, "rb") as f:
                private_key = serialization.load_pem_private_key(f.read(), password=None)
            
            # Check if certificate is expired or will expire soon (within 30 days)
            now = datetime.utcnow()
            expires_soon = now + timedelta(days=30)
            
            if now >= cert.not_valid_after:
                logger.warning(f"SSL certificate has expired on {cert.not_valid_after}")
                return "expired"
            elif expires_soon >= cert.not_valid_after:
                logger.warning(f"SSL certificate expires soon on {cert.not_valid_after}")
                logger.info("Certificate will be regenerated for extended validity")
                return "expired"  # Treat expiring soon as expired to regenerate
                
            # Check if certificate is not yet valid
            if now < cert.not_valid_before:
                logger.warning(f"SSL certificate is not yet valid (valid from {cert.not_valid_before})")
                return "invalid"
                
            logger.info(f"SSL certificate is valid until {cert.not_valid_after}")
            return "valid"
            
        except ImportError:
            # If cryptography is not available, just check file existence
            logger.warning("Cannot validate certificate format - cryptography library not available")
            return "valid"  # Assume valid if we can't check
            
    except Exception as e:
        logger.error(f"Certificate validation failed: {e}")
        return "invalid"


def create_ssl_context(cert_path: str, key_path: str):
    """
    Create SSL context for Flask application
    
    Args:
        cert_path: Path to certificate file
        key_path: Path to private key file
        
    Returns:
        SSL context tuple (cert_path, key_path) or None if invalid
    """
    try:
        validation_result = _validate_certificate_files(cert_path, key_path)
        if validation_result == "valid":
            logger.info(f"Creating SSL context with cert: {cert_path}, key: {key_path}")
            return (cert_path, key_path)
        else:
            logger.error(f"Invalid SSL certificate files: {validation_result}")
            return None
    except Exception as e:
        logger.error(f"Failed to create SSL context: {e}")
        return None


def create_requests_session_with_ssl_support(verify_ssl: bool = True, ca_cert_path: str = None):
    """
    Create a requests session that can handle self-signed certificates
    
    Args:
        verify_ssl: Whether to verify SSL certificates
        ca_cert_path: Path to custom CA certificate bundle
        
    Returns:
        Configured requests session
    """
    import requests
    from requests.adapters import HTTPAdapter
    from requests.packages.urllib3.util.retry import Retry
    
    session = requests.Session()
    
    # Configure SSL verification
    if not verify_ssl:
        # Disable SSL verification for self-signed certificates
        session.verify = False
        # Suppress SSL warnings
        import urllib3
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        logger.info("SSL verification disabled for API requests")
    elif ca_cert_path and os.path.exists(ca_cert_path):
        # Use custom CA certificate
        session.verify = ca_cert_path
        logger.info(f"Using custom CA certificate: {ca_cert_path}")
    else:
        # Use default SSL verification
        session.verify = True
        logger.info("Using default SSL verification")
    
    # Configure retry strategy
    retry_strategy = Retry(
        total=3,
        status_forcelist=[429, 500, 502, 503, 504],
        method_whitelist=["HEAD", "GET", "OPTIONS", "POST", "PUT", "DELETE"]
    )
    
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    
    return session