//! Listener (frontend) types

use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{LoadBalancerId, PoolId};

/// Unique identifier for a listener
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ListenerId(Uuid);

impl ListenerId {
    /// Create a new random ListenerId
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }

    /// Create from existing UUID
    pub fn from_uuid(uuid: Uuid) -> Self {
        Self(uuid)
    }

    /// Get the inner UUID
    pub fn as_uuid(&self) -> &Uuid {
        &self.0
    }
}

impl Default for ListenerId {
    fn default() -> Self {
        Self::new()
    }
}

impl std::fmt::Display for ListenerId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Listener protocol
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum ListenerProtocol {
    /// TCP (L4)
    #[default]
    Tcp,
    /// UDP (L4)
    Udp,
    /// HTTP (L7)
    Http,
    /// HTTPS (L7 with TLS termination)
    Https,
    /// Terminated HTTPS (pass through to HTTP backend)
    TerminatedHttps,
}


/// TLS configuration for HTTPS listeners
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
    /// Certificate ID (reference to certificate store)
    pub certificate_id: String,
    /// Minimum TLS version
    pub min_version: TlsVersion,
    /// Cipher suites (empty = use defaults)
    pub cipher_suites: Vec<String>,
}

/// TLS version
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum TlsVersion {
    #[default]
    Tls12,
    Tls13,
}


/// Listener - frontend entry point
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Listener {
    /// Unique identifier
    pub id: ListenerId,
    /// Human-readable name
    pub name: String,
    /// Parent load balancer
    pub loadbalancer_id: LoadBalancerId,
    /// Protocol
    pub protocol: ListenerProtocol,
    /// Listen port
    pub port: u16,
    /// Default pool for traffic
    pub default_pool_id: Option<PoolId>,
    /// TLS configuration (for HTTPS)
    pub tls_config: Option<TlsConfig>,
    /// Connection limit (0 = unlimited)
    pub connection_limit: u32,
    /// Enabled state
    pub enabled: bool,
    /// Creation timestamp
    pub created_at: u64,
    /// Last update timestamp
    pub updated_at: u64,
}

impl Listener {
    /// Create a new listener
    pub fn new(
        name: impl Into<String>,
        loadbalancer_id: LoadBalancerId,
        protocol: ListenerProtocol,
        port: u16,
    ) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();
        
        Self {
            id: ListenerId::new(),
            name: name.into(),
            loadbalancer_id,
            protocol,
            port,
            default_pool_id: None,
            tls_config: None,
            connection_limit: 0,
            enabled: true,
            created_at: now,
            updated_at: now,
        }
    }

    /// Check if this is an L7 protocol
    pub fn is_l7(&self) -> bool {
        matches!(
            self.protocol,
            ListenerProtocol::Http | ListenerProtocol::Https | ListenerProtocol::TerminatedHttps
        )
    }
}

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

    #[test]
    fn test_listener_creation() {
        let lb_id = LoadBalancerId::new();
        let listener = Listener::new("http-frontend", lb_id, ListenerProtocol::Http, 80);
        assert_eq!(listener.name, "http-frontend");
        assert_eq!(listener.port, 80);
        assert!(listener.is_l7());
    }

    #[test]
    fn test_l7_detection() {
        let lb_id = LoadBalancerId::new();
        
        let tcp = Listener::new("tcp", lb_id, ListenerProtocol::Tcp, 8080);
        assert!(!tcp.is_l7());
        
        let http = Listener::new("http", lb_id, ListenerProtocol::Http, 80);
        assert!(http.is_l7());
        
        let https = Listener::new("https", lb_id, ListenerProtocol::Https, 443);
        assert!(https.is_l7());
    }
}
