//! Domain entities for infrastructure management
//!
//! This module provides domain-driven design entities for managing infrastructure.
//! These types represent the business domain concepts, separate from API details.

use serde::{Deserialize, Serialize};
use std::fmt;

/// Unique identifier for a server
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ServerId(String);

impl ServerId {
    pub fn new(id: String) -> Self {
        Self(id)
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

/// Server type/role
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ServerType {
    Master,
    Worker,
    Database,
}

impl fmt::Display for ServerType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ServerType::Master => write!(f, "master"),
            ServerType::Worker => write!(f, "worker"),
            ServerType::Database => write!(f, "database"),
        }
    }
}

/// Server status
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ServerStatus {
    Running,
    Stopped,
    Starting,
    Stopping,
    Unknown,
}

/// Server specification (desired state)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerSpec {
    name: String,
    server_type: ServerType,
    size: String,
    location: String,
    image: String,
    ssh_keys: Vec<String>,
    labels: Vec<(String, String)>,
    attach_to_network: Option<String>, // Network name to attach to
}

impl ServerSpec {
    pub fn new(
        name: String,
        server_type: ServerType,
        size: String,
        location: String,
        image: String,
    ) -> Self {
        Self {
            name,
            server_type,
            size,
            location,
            image,
            ssh_keys: Vec::new(),
            labels: Vec::new(),
            attach_to_network: None,
        }
    }

    pub fn with_ssh_keys(mut self, ssh_keys: Vec<String>) -> Self {
        self.ssh_keys = ssh_keys;
        self
    }

    pub fn with_labels(mut self, labels: Vec<(String, String)>) -> Self {
        self.labels = labels;
        self
    }

    pub fn with_network(mut self, network_name: String) -> Self {
        self.attach_to_network = Some(network_name);
        self
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn server_type(&self) -> &ServerType {
        &self.server_type
    }

    pub fn size(&self) -> &str {
        &self.size
    }

    pub fn location(&self) -> &str {
        &self.location
    }

    pub fn image(&self) -> &str {
        &self.image
    }

    pub fn ssh_keys(&self) -> &[String] {
        &self.ssh_keys
    }

    pub fn labels(&self) -> &[(String, String)] {
        &self.labels
    }

    pub fn network(&self) -> Option<&str> {
        self.attach_to_network.as_deref()
    }
}

/// Server entity (actual state)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Server {
    id: ServerId,
    spec: ServerSpec,
    status: ServerStatus,
    public_ipv4: Option<String>,
    private_ipv4: Option<String>,
}

impl Server {
    pub fn new(id: ServerId, spec: ServerSpec, status: ServerStatus) -> Self {
        Self {
            id,
            spec,
            status,
            public_ipv4: None,
            private_ipv4: None,
        }
    }

    pub fn with_public_ipv4(mut self, ip: String) -> Self {
        self.public_ipv4 = Some(ip);
        self
    }

    pub fn with_private_ipv4(mut self, ip: String) -> Self {
        self.private_ipv4 = Some(ip);
        self
    }

    pub fn id(&self) -> &ServerId {
        &self.id
    }

    pub fn spec(&self) -> &ServerSpec {
        &self.spec
    }

    pub fn status(&self) -> &ServerStatus {
        &self.status
    }

    pub fn public_ipv4(&self) -> Option<&str> {
        self.public_ipv4.as_deref()
    }

    pub fn private_ipv4(&self) -> Option<&str> {
        self.private_ipv4.as_deref()
    }

    pub fn name(&self) -> &str {
        self.spec.name()
    }
}

/// Unique identifier for a network
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NetworkId(String);

impl NetworkId {
    pub fn new(id: String) -> Self {
        Self(id)
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

/// Subnet specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubnetSpec {
    pub network_zone: String,
    pub ip_range: String,
}

/// Network specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkSpec {
    name: String,
    ip_range: String,
    subnets: Vec<SubnetSpec>,
}

impl NetworkSpec {
    pub fn new(name: String, ip_range: String) -> Self {
        Self {
            name,
            ip_range,
            subnets: Vec::new(),
        }
    }

    pub fn with_subnets(mut self, subnets: Vec<SubnetSpec>) -> Self {
        self.subnets = subnets;
        self
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn ip_range(&self) -> &str {
        &self.ip_range
    }

    pub fn subnets(&self) -> &[SubnetSpec] {
        &self.subnets
    }
}

/// Network entity
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Network {
    id: NetworkId,
    spec: NetworkSpec,
}

impl Network {
    pub fn new(id: NetworkId, spec: NetworkSpec) -> Self {
        Self { id, spec }
    }

    pub fn id(&self) -> &NetworkId {
        &self.id
    }

    pub fn spec(&self) -> &NetworkSpec {
        &self.spec
    }

    pub fn name(&self) -> &str {
        self.spec.name()
    }
}

/// Unique identifier for a firewall
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FirewallId(String);

impl FirewallId {
    pub fn new(id: String) -> Self {
        Self(id)
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

/// Firewall rule
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FirewallRule {
    pub direction: String,
    pub source_ips: Vec<String>,
    pub destination_ips: Vec<String>,
    pub protocol: String,
    pub port: Option<String>,
    pub description: Option<String>,
}

impl FirewallRule {
    pub fn new(direction: String, protocol: String) -> Self {
        Self {
            direction,
            source_ips: Vec::new(),
            destination_ips: Vec::new(),
            protocol,
            port: None,
            description: None,
        }
    }

    pub fn with_source_ips(mut self, ips: Vec<String>) -> Self {
        self.source_ips = ips;
        self
    }

    pub fn with_port(mut self, port: String) -> Self {
        self.port = Some(port);
        self
    }

    pub fn with_description(mut self, desc: String) -> Self {
        self.description = Some(desc);
        self
    }
}

/// Firewall specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FirewallSpec {
    name: String,
    rules: Vec<FirewallRule>,
    apply_to: Vec<String>,
}

impl FirewallSpec {
    pub fn new(name: String) -> Self {
        Self {
            name,
            rules: Vec::new(),
            apply_to: Vec::new(),
        }
    }

    pub fn with_rules(mut self, rules: Vec<FirewallRule>) -> Self {
        self.rules = rules;
        self
    }

    pub fn with_apply_to(mut self, apply_to: Vec<String>) -> Self {
        self.apply_to = apply_to;
        self
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn rules(&self) -> &[FirewallRule] {
        &self.rules
    }

    pub fn apply_to(&self) -> &[String] {
        &self.apply_to
    }
}

/// Firewall entity
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Firewall {
    id: FirewallId,
    spec: FirewallSpec,
}

impl Firewall {
    pub fn new(id: FirewallId, spec: FirewallSpec) -> Self {
        Self { id, spec }
    }

    pub fn id(&self) -> &FirewallId {
        &self.id
    }

    pub fn spec(&self) -> &FirewallSpec {
        &self.spec
    }

    pub fn name(&self) -> &str {
        self.spec.name()
    }
}

/// Unique identifier for a load balancer
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct LoadBalancerId(String);

impl LoadBalancerId {
    pub fn new(id: String) -> Self {
        Self(id)
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

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

/// Load balancer specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadBalancerSpec {
    name: String,
    load_balancer_type: String,
    location: String,
    algorithm: String,
}

impl LoadBalancerSpec {
    pub fn new(name: String, load_balancer_type: String, location: String) -> Self {
        Self {
            name,
            load_balancer_type,
            location,
            algorithm: "round_robin".to_string(),
        }
    }

    pub fn with_algorithm(mut self, algorithm: String) -> Self {
        self.algorithm = algorithm;
        self
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn load_balancer_type(&self) -> &str {
        &self.load_balancer_type
    }

    pub fn location(&self) -> &str {
        &self.location
    }

    pub fn algorithm(&self) -> &str {
        &self.algorithm
    }
}

/// Load balancer entity
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoadBalancer {
    id: LoadBalancerId,
    spec: LoadBalancerSpec,
    public_ipv4: Option<String>,
}

impl LoadBalancer {
    pub fn new(id: LoadBalancerId, spec: LoadBalancerSpec) -> Self {
        Self {
            id,
            spec,
            public_ipv4: None,
        }
    }

    pub fn with_public_ipv4(mut self, ip: String) -> Self {
        self.public_ipv4 = Some(ip);
        self
    }

    pub fn id(&self) -> &LoadBalancerId {
        &self.id
    }

    pub fn spec(&self) -> &LoadBalancerSpec {
        &self.spec
    }

    pub fn public_ipv4(&self) -> Option<&str> {
        self.public_ipv4.as_deref()
    }

    pub fn name(&self) -> &str {
        self.spec.name()
    }
}

/// Complete infrastructure specification
#[derive(Debug, Clone)]
pub struct InfrastructureSpec {
    pub network: NetworkSpec,
    pub firewall: FirewallSpec,
    pub loadbalancer: LoadBalancerSpec,
    pub servers: Vec<ServerSpec>,
}

impl InfrastructureSpec {
    pub fn new(
        network: NetworkSpec,
        firewall: FirewallSpec,
        loadbalancer: LoadBalancerSpec,
        servers: Vec<ServerSpec>,
    ) -> Self {
        Self {
            network,
            firewall,
            loadbalancer,
            servers,
        }
    }

    pub fn network(&self) -> &NetworkSpec {
        &self.network
    }

    pub fn firewall(&self) -> &FirewallSpec {
        &self.firewall
    }

    pub fn loadbalancer(&self) -> &LoadBalancerSpec {
        &self.loadbalancer
    }

    pub fn servers(&self) -> &[ServerSpec] {
        &self.servers
    }
}

/// Type of difference
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DiffType {
    Create,
    Update,
    Delete,
    NoChange,
}

/// Individual diff item
#[derive(Debug, Clone)]
pub struct DiffItem {
    resource_type: String,
    resource_name: String,
    diff_type: DiffType,
    details: Vec<String>,
}

impl DiffItem {
    pub fn new(resource_type: String, resource_name: String, diff_type: DiffType) -> Self {
        Self {
            resource_type,
            resource_name,
            diff_type,
            details: Vec::new(),
        }
    }

    pub fn with_details(mut self, details: Vec<String>) -> Self {
        self.details = details;
        self
    }

    pub fn resource_type(&self) -> &str {
        &self.resource_type
    }

    pub fn resource_name(&self) -> &str {
        &self.resource_name
    }

    pub fn diff_type(&self) -> &DiffType {
        &self.diff_type
    }

    pub fn details(&self) -> &[String] {
        &self.details
    }
}

/// Infrastructure diff result
#[derive(Debug, Clone)]
pub struct InfrastructureDiff {
    items: Vec<DiffItem>,
}

impl InfrastructureDiff {
    pub fn new(items: Vec<DiffItem>) -> Self {
        Self { items }
    }

    pub fn items(&self) -> &[DiffItem] {
        &self.items
    }

    pub fn has_changes(&self) -> bool {
        self.items
            .iter()
            .any(|item| item.diff_type != DiffType::NoChange)
    }

    pub fn creates(&self) -> Vec<&DiffItem> {
        self.items
            .iter()
            .filter(|item| item.diff_type == DiffType::Create)
            .collect()
    }

    pub fn updates(&self) -> Vec<&DiffItem> {
        self.items
            .iter()
            .filter(|item| item.diff_type == DiffType::Update)
            .collect()
    }

    pub fn deletes(&self) -> Vec<&DiffItem> {
        self.items
            .iter()
            .filter(|item| item.diff_type == DiffType::Delete)
            .collect()
    }
}

/// Current infrastructure state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InfrastructureState {
    pub servers: Vec<Server>,
    pub networks: Vec<Network>,
    pub firewalls: Vec<Firewall>,
    pub load_balancers: Vec<LoadBalancer>,
}

impl InfrastructureState {
    pub fn new() -> Self {
        Self {
            servers: Vec::new(),
            networks: Vec::new(),
            firewalls: Vec::new(),
            load_balancers: Vec::new(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.servers.is_empty()
            && self.networks.is_empty()
            && self.firewalls.is_empty()
            && self.load_balancers.is_empty()
    }
}

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