//! Port types

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

use crate::{SecurityGroupId, SubnetId};

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

impl PortId {
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }

    pub fn from_uuid(uuid: Uuid) -> Self {
        Self(uuid)
    }

    pub fn as_uuid(&self) -> &Uuid {
        &self.0
    }
}

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

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

/// Port status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum PortStatus {
    #[default]
    Build,
    Active,
    Down,
    Error,
}


/// Device type attached to port
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum DeviceType {
    #[default]
    None,
    Vm,
    Router,
    LoadBalancer,
    DhcpServer,
    Other,
}


/// Network port (vNIC)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Port {
    pub id: PortId,
    pub subnet_id: SubnetId,
    pub name: String,
    pub description: Option<String>,
    pub mac_address: String,
    pub ip_address: Option<String>,
    pub device_id: Option<String>,
    pub device_type: DeviceType,
    pub security_groups: Vec<SecurityGroupId>,
    pub admin_state_up: bool,
    pub status: PortStatus,
    pub created_at: u64,
    pub updated_at: u64,
}

impl Port {
    pub fn new(name: impl Into<String>, subnet_id: SubnetId) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();

        Self {
            id: PortId::new(),
            subnet_id,
            name: name.into(),
            description: None,
            mac_address: Self::generate_mac(),
            ip_address: None,
            device_id: None,
            device_type: DeviceType::None,
            security_groups: Vec::new(),
            admin_state_up: true,
            status: PortStatus::Build,
            created_at: now,
            updated_at: now,
        }
    }

    /// Generate a random MAC address (locally administered)
    fn generate_mac() -> String {
        use std::time::{SystemTime, UNIX_EPOCH};
        let seed = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_nanos() as u64;

        // Set locally administered bit (bit 1 of first octet)
        // Clear multicast bit (bit 0 of first octet)
        let first = ((seed >> 40) as u8 & 0xFC) | 0x02;

        format!(
            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
            first,
            (seed >> 32) as u8,
            (seed >> 24) as u8,
            (seed >> 16) as u8,
            (seed >> 8) as u8,
            seed as u8
        )
    }
}

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

    #[test]
    fn test_port_creation() {
        let subnet_id = SubnetId::new();
        let port = Port::new("test-port", subnet_id);
        assert_eq!(port.name, "test-port");
        assert!(port.admin_state_up);
        assert_eq!(port.status, PortStatus::Build);
        assert!(!port.mac_address.is_empty());
    }

    #[test]
    fn test_mac_generation() {
        let mac = Port::generate_mac();
        assert_eq!(mac.len(), 17); // XX:XX:XX:XX:XX:XX
        let first_octet = u8::from_str_radix(&mac[0..2], 16).unwrap();
        assert_eq!(first_octet & 0x01, 0); // Not multicast
        assert_eq!(first_octet & 0x02, 0x02); // Locally administered
    }
}
