use crate::libvirt_lib_structures::XMLUuid;
use std::net::{IpAddr, Ipv4Addr};

/// Network forward information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "forward")]
pub struct NetworkForwardXML {
    #[serde(rename = "@mode")]
    pub mode: String,
    #[serde(default, rename = "@dev", skip_serializing_if = "String::is_empty")]
    pub dev: String,
}

/// Network bridge information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "bridge")]
pub struct NetworkBridgeXML {
    #[serde(rename = "@name")]
    pub name: String,
}

/// Network DNS information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "dns")]
pub struct NetworkDNSXML {
    pub forwarder: NetworkDNSForwarderXML,
}

/// Network DNS information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "forwarder")]
pub struct NetworkDNSForwarderXML {
    /// Address of the DNS server
    #[serde(rename = "@addr")]
    pub addr: Ipv4Addr,
}

/// Network DNS information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "domain")]
pub struct NetworkDomainXML {
    #[serde(rename = "@name")]
    pub name: String,
}

/// Network ip information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "ip")]
pub struct NetworkIPXML {
    #[serde(default, rename = "@family")]
    pub family: String,
    #[serde(rename = "@address")]
    pub address: IpAddr,
    /// Network Prefix
    #[serde(rename = "@prefix")]
    pub prefix: Option<u8>,
    /// Network Netmask. This field is never serialized, but because we can't know if LibVirt will
    /// provide us netmask or prefix, we need to handle both of these fields
    #[serde(rename = "@netmask", skip_serializing)]
    pub netmask: Option<IpAddr>,

    pub dhcp: Option<NetworkDHCPXML>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "dhcp")]
pub struct NetworkDHCPXML {
    pub range: NetworkDHCPRangeXML,
    #[serde(default, rename = "host", skip_serializing_if = "Vec::is_empty")]
    pub hosts: Vec<NetworkDHCPHostXML>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "host")]
pub struct NetworkDHCPHostXML {
    #[serde(rename = "@mac", default, skip_serializing_if = "Option::is_none")]
    pub mac: Option<String>,
    #[serde(rename = "@name")]
    pub name: String,
    #[serde(rename = "@ip")]
    pub ip: IpAddr,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "dhcp")]
pub struct NetworkDHCPRangeXML {
    #[serde(rename = "@start")]
    pub start: IpAddr,
    #[serde(rename = "@end")]
    pub end: IpAddr,
}

/// Network information, see https://libvirt.org/formatnetwork.html
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "network")]
pub struct NetworkXML {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub uuid: Option<XMLUuid>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub forward: Option<NetworkForwardXML>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bridge: Option<NetworkBridgeXML>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dns: Option<NetworkDNSXML>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub domain: Option<NetworkDomainXML>,
    #[serde(default, rename = "ip")]
    pub ips: Vec<NetworkIPXML>,
}

impl NetworkXML {
    pub fn parse_xml(xml: &str) -> anyhow::Result<Self> {
        Ok(quick_xml::de::from_str(xml)?)
    }

    pub fn as_xml(&self) -> anyhow::Result<String> {
        Ok(quick_xml::se::to_string(self)?)
    }
}