"""
VPN Configuration Models

This module contains models for managing VPN configurations specific
to channel-zone relationships. Each VPN technology has its own model
with technology-specific parameters.

Models:
    - VPNConfiguration: Abstract base for VPN configurations
    - IPSecConfiguration: IPSec-specific VPN configuration
    - OpenVPNConfiguration: OpenVPN-specific configuration
    - WireGuardConfiguration: WireGuard-specific configuration
"""

from django.db import models
from django.core.validators import RegexValidator
from django.utils.translation import gettext_lazy as _

from apps.common.models import BaseModel
from apps.channels.models.channel_relations import ChannelZoneRelation


class VPNConfiguration(BaseModel):
    """
    Abstract base for VPN-specific configurations.
    
    Extended by concrete VPN implementation models to store
    technology-specific configuration parameters.
    """
    
    zone_relation = models.OneToOneField(
        ChannelZoneRelation,
        on_delete=models.CASCADE,
        related_name='%(class)s_config',
        verbose_name=_('Zone Relation'),
        help_text=_('The zone relation this VPN configuration applies to')
    )
  
    is_enabled = models.BooleanField(
        default=True,
        verbose_name=_('Is Enabled'),
        help_text=_('Whether this VPN configuration is currently enabled')
    )
    
    class Meta:
        abstract = True
        

class IPSecConfiguration(VPNConfiguration):
    """
    IPSec VPN configuration for channel-zone relationships.
    
    Stores IPSec-specific parameters including pre-shared keys,
    subnet configurations, and tunnel settings.
    
    Attributes:
        preshared_key (str): IPSec pre-shared key for authentication
        local_subnet (str): Local network subnet (e.g., 192.168.1.0/24)
        remote_subnet (str): Remote network subnet (e.g., 10.0.0.0/24)
        encryption_algorithm (str): Encryption algorithm to use
        hash_algorithm (str): Hash algorithm for integrity
        
    Relationships:
        - zone_relation: One-to-one with ChannelZoneRelation
        
    Example:
        >>> ipsec_config = IPSecConfiguration.objects.create(
        ...     zone_relation=my_relation,
        ...     preshared_key="secure_key_here",
        ...     local_subnet="192.168.1.0/24",
        ...     remote_subnet="10.0.0.0/24"
        ... )
    """
    
    ENCRYPTION_ALGORITHMS = [
        ('aes128', 'AES-128'),
        ('aes192', 'AES-192'),
        ('aes256', 'AES-256'),
        ('3des', '3DES'),
    ]
    
    HASH_ALGORITHMS = [
        ('sha1', 'SHA-1'),
        ('sha256', 'SHA-256'),
        ('sha384', 'SHA-384'),
        ('sha512', 'SHA-512'),
        ('md5', 'MD5'),
    ]
    
    preshared_key = models.TextField(
        verbose_name=_('Pre-shared Key'),
        help_text=_('IPSec pre-shared key for authentication')
    )
    local_subnet = models.CharField(
        max_length=18,
        verbose_name=_('Local Subnet'),
        help_text=_('Local network subnet in CIDR notation (e.g., 192.168.1.0/24)'),
        validators=[
            RegexValidator(
                regex=r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$',
                message=_('Enter a valid CIDR subnet (e.g., 192.168.1.0/24)')
            )
        ]
    )
    remote_subnet = models.CharField(
        max_length=18,
        verbose_name=_('Remote Subnet'),
        help_text=_('Remote network subnet in CIDR notation (e.g., 10.0.0.0/24)'),
        validators=[
            RegexValidator(
                regex=r'^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$',
                message=_('Enter a valid CIDR subnet (e.g., 10.0.0.0/24)')
            )
        ]
    )
    encryption_algorithm = models.CharField(
        max_length=20,
        choices=ENCRYPTION_ALGORITHMS,
        default='aes256',
        verbose_name=_('Encryption Algorithm'),
        help_text=_('Encryption algorithm for IPSec tunnel')
    )
    hash_algorithm = models.CharField(
        max_length=20,
        choices=HASH_ALGORITHMS,
        default='sha256',
        verbose_name=_('Hash Algorithm'),
        help_text=_('Hash algorithm for integrity verification')
    )
    
    class Meta:
        db_table = "ipsec_configurations"
        verbose_name = _('IPSec Configuration')
        verbose_name_plural = _('IPSec Configurations')
    
    def __str__(self):
        return f"IPSec: {self.zone_relation}"


class OpenVPNConfiguration(VPNConfiguration):
    """
    OpenVPN configuration for channel-zone relationships.
    
    Stores OpenVPN-specific parameters including certificates,
    configuration files, and connection settings.
    
    Attributes:
        config_file (FileField): OpenVPN configuration file
        ca_cert (str): Certificate Authority certificate
        client_cert (str): Client certificate
        client_key (str): Client private key
        compression (str): Compression algorithm
        cipher (str): Encryption cipher
        
    Relationships:
        - zone_relation: One-to-one with ChannelZoneRelation
        
    Example:
        >>> openvpn_config = OpenVPNConfiguration.objects.create(
        ...     zone_relation=my_relation,
        ...     ca_cert="-----BEGIN CERTIFICATE-----...",
        ...     client_cert="-----BEGIN CERTIFICATE-----...",
        ...     client_key="-----BEGIN PRIVATE KEY-----..."
        ... )
    """
    
    COMPRESSION_ALGORITHMS = [
        ('none', 'No Compression'),
        ('lzo', 'LZO Compression'),
        ('lz4', 'LZ4 Compression'),
    ]
    
    CIPHER_ALGORITHMS = [
        ('aes-256-cbc', 'AES-256-CBC'),
        ('aes-192-cbc', 'AES-192-CBC'),
        ('aes-128-cbc', 'AES-128-CBC'),
        ('aes-256-gcm', 'AES-256-GCM'),
        ('chacha20-poly1305', 'ChaCha20-Poly1305'),
    ]
    
    config_file = models.FileField(
        upload_to='vpn_configs/openvpn/',
        blank=True,
        null=True,
        verbose_name=_('OpenVPN Config File'),
        help_text=_('OpenVPN configuration file (.ovpn)')
    )
    ca_cert = models.TextField(
        blank=True,
        verbose_name=_('CA Certificate'),
        help_text=_('Certificate Authority certificate in PEM format')
    )
    client_cert = models.TextField(
        blank=True,
        verbose_name=_('Client Certificate'),
        help_text=_('Client certificate in PEM format')
    )
    client_key = models.TextField(
        blank=True,
        verbose_name=_('Client Private Key'),
        help_text=_('Client private key in PEM format')
    )
    compression = models.CharField(
        max_length=20,
        choices=COMPRESSION_ALGORITHMS,
        default='lzo',
        verbose_name=_('Compression'),
        help_text=_('Compression algorithm for OpenVPN tunnel')
    )
    cipher = models.CharField(
        max_length=30,
        choices=CIPHER_ALGORITHMS,
        default='aes-256-cbc',
        verbose_name=_('Cipher'),
        help_text=_('Encryption cipher for OpenVPN tunnel')
    )
    
    class Meta:
        db_table = "openvpn_configurations"
        verbose_name = _('OpenVPN Configuration')
        verbose_name_plural = _('OpenVPN Configurations')
    
    def __str__(self):
        return f"OpenVPN: {self.zone_relation}"
    
    def has_certificates(self):
        """
        Check if all required certificates are configured.
        
        Returns:
            bool: True if CA cert, client cert, and key are present
        """
        return bool(self.ca_cert and self.client_cert and self.client_key)


class WireGuardConfiguration(VPNConfiguration):
    """
    WireGuard VPN configuration for channel-zone relationships.
    
    Stores WireGuard-specific parameters including cryptographic keys,
    endpoints, and allowed IP ranges.
    
    Attributes:
        private_key (str): WireGuard private key
        public_key (str): WireGuard public key
        peer_public_key (str): Peer's public key
        endpoint (str): Server endpoint (host:port)
        allowed_ips (str): Comma-separated allowed IP ranges
        persistent_keepalive (int): Keepalive interval in seconds
        
    Relationships:
        - zone_relation: One-to-one with ChannelZoneRelation
        
    Example:
        >>> wireguard_config = WireGuardConfiguration.objects.create(
        ...     zone_relation=my_relation,
        ...     private_key="private_key_here",
        ...     peer_public_key="peer_public_key_here",
        ...     endpoint="vpn.example.com:51820",
        ...     allowed_ips="0.0.0.0/0"
        ... )
    """
    
    private_key = models.TextField(
        verbose_name=_('Private Key'),
        help_text=_('WireGuard private key for this client')
    )
    public_key = models.TextField(
        blank=True,
        verbose_name=_('Public Key'),
        help_text=_('WireGuard public key for this client (auto-generated from private key)')
    )
    peer_public_key = models.TextField(
        verbose_name=_('Peer Public Key'),
        help_text=_('WireGuard public key of the VPN server')
    )
    endpoint = models.CharField(
        max_length=255,
        verbose_name=_('Endpoint'),
        help_text=_('WireGuard server endpoint (host:port, e.g., vpn.example.com:51820)')
    )
    allowed_ips = models.TextField(
        default='0.0.0.0/0',
        verbose_name=_('Allowed IPs'),
        help_text=_('Comma-separated list of allowed IP ranges (CIDR notation)')
    )
    persistent_keepalive = models.PositiveIntegerField(
        default=25,
        verbose_name=_('Persistent Keepalive'),
        help_text=_('Keepalive interval in seconds (0 to disable)')
    )
    
    class Meta:
        db_table = "wireguard_configurations"
        verbose_name = _('WireGuard Configuration')
        verbose_name_plural = _('WireGuard Configurations')
    
    def __str__(self):
        return f"WireGuard: {self.zone_relation}"
    
    def save(self, *args, **kwargs):
        """
        Auto-generate public key from private key if not provided.
        Note: This is a placeholder - actual implementation would require
        WireGuard cryptography library.
        """
        if self.private_key and not self.public_key:
            # In real implementation, derive public key from private key
            # self.public_key = derive_public_key(self.private_key)
            pass
        super().save(*args, **kwargs)
    
    def get_allowed_ips_list(self):
        """
        Parse allowed IPs string into a list.
        
        Returns:
            list: List of allowed IP ranges
        """
        if not self.allowed_ips:
            return []
        return [ip.strip() for ip in self.allowed_ips.split(',') if ip.strip()]

 
