//! Network configuration service messages

use core::net::IpAddr;

use abi_serde::{
    des::{DeserializeError, Deserializer},
    ser::Serializer,
    Deserialize, Serialize,
};
use alloc::boxed::Box;

use super::{link::LinkState, MacAddress, SocketInterfaceQuery, SubnetAddr};

/// Describes the binding between interfaces and their IDs
#[derive(Clone, Debug, PartialEq)]
pub struct InterfaceBinding {
    /// Name of the interface
    pub name: Box<str>,
    /// Interface ID
    pub id: u32,
}
abi_serde::impl_struct_serde!(InterfaceBinding: [
    name,
    id
]);

/// Describes a single route
#[derive(Clone, Debug, PartialEq)]
// #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RouteInfo {
    /// Interface textual name (unique)
    pub interface_name: Box<str>,
    /// Interface unique ID number
    pub interface_id: u32,
    /// Subnetwork this route is related to
    pub subnet: SubnetAddr,
    /// Gateway this route should be reached through
    pub gateway: Option<IpAddr>,
}
abi_serde::impl_struct_serde!(RouteInfo: [
    interface_name,
    interface_id,
    subnet,
    gateway
]);

/// Describes routing information about some address
#[derive(Clone, Debug, PartialEq)]
pub struct RoutingInfo {
    /// Interface textual name (unique)
    pub interface_name: Box<str>,
    /// Interface unique ID number
    pub interface_id: u32,
    /// Gateway this route should be reached through
    pub gateway: Option<IpAddr>,
    /// Real destination which should be used
    pub destination: IpAddr,
    /// IP address used as a source when sending through this route
    pub source: Option<IpAddr>,
    /// Interface MAC address
    pub source_mac: MacAddress,
}
abi_serde::impl_struct_serde!(RoutingInfo: [
    interface_name,
    interface_id,
    gateway,
    destination,
    source,
    source_mac
]);

/// Describes a single interface
#[derive(Clone, Debug, PartialEq)]
pub struct InterfaceInfo {
    /// Interface textual name (unique)
    pub interface_name: Box<str>,
    /// Interface unique ID number
    pub interface_id: u32,
    /// Interface IP address
    pub address: Option<IpAddr>,
    /// Interface hardware address
    pub mac: MacAddress,
    /// Interface link status
    pub link: LinkState,
}
abi_serde::impl_struct_serde!(InterfaceInfo: [
    interface_name,
    interface_id,
    address,
    mac,
    link
]);

/// Describes a request to add a route to an interface
#[derive(Clone, Debug, PartialEq)]
pub struct AddRouteRequest<'a> {
    /// Interface to which the action applies
    pub interface: SocketInterfaceQuery<'a>,
    /// Optional gateway to reach the route through
    pub gateway: Option<IpAddr>,
    /// Subnetwork of the route
    pub subnet: SubnetAddr,
}
abi_serde::impl_struct_serde!(AddRouteRequest<'de>: [
    interface,
    gateway,
    subnet
]);

/// Describes a request to assign a network address to an interface
#[derive(Clone, Debug, PartialEq)]
pub struct SetNetworkAddressRequest<'a> {
    /// Interface to which the action applies
    pub interface: SocketInterfaceQuery<'a>,
    /// Address to assign
    pub address: IpAddr,
}
abi_serde::impl_struct_serde!(SetNetworkAddressRequest<'de>: [
    interface,
    address
]);

/// Describes a request to perform an ARP query
#[derive(Clone, Debug, PartialEq)]
pub struct ArpQueryRequest<'a> {
    /// Interface to which the action applies
    pub interface: SocketInterfaceQuery<'a>,
    /// Address to query
    pub address: IpAddr,
    /// Whether to actually send any requests, or just lookup the cached result
    pub cache_only: bool,
}
abi_serde::impl_struct_serde!(ArpQueryRequest<'de>: [
    interface,
    address,
    cache_only
]);

/// kernel-netconf interface request
#[derive(Clone, Debug, PartialEq)]
pub enum NetConfigRequest<'a> {
    /// List all routes
    ListRoutes(()),
    /// List all interfaces
    ListInterfaces(()),
    /// Describe routes related to a single interface
    DescribeRoutes(SocketInterfaceQuery<'a>),
    /// Describe a single interface
    DescribeInterface(SocketInterfaceQuery<'a>),
    /// Add a route
    AddRoute(AddRouteRequest<'a>),
    /// Set interface's IP address
    SetNetworkAddress(SetNetworkAddressRequest<'a>),
    /// Clear interface's IP address
    ClearNetworkAddress(SocketInterfaceQuery<'a>),
    /// Query a route and return routing information
    QueryRoute(IpAddr),
    /// Query the MAC address of the specified IP
    ArpQuery(ArpQueryRequest<'a>),
}
abi_serde::impl_enum1_serde!(NetConfigRequest<'de>: [
    ListRoutes => 1,
    ListInterfaces => 2,
    DescribeRoutes => 3,
    DescribeInterface => 4,
    AddRoute => 5,
    SetNetworkAddress => 6,
    ClearNetworkAddress => 7,
    QueryRoute => 8,
    ArpQuery => 9,
]);

/// Wrapper to provide error status in replies
#[derive(Clone, Debug, PartialEq)]
pub enum NetConfigResult<'a, T> {
    /// Success
    Ok(T),
    /// Error
    Err(&'a str),
}

impl<T: Serialize> Serialize for NetConfigResult<'_, T> {
    fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
        match self {
            Self::Ok(value) => {
                serializer.write_enum_variant(0)?;
                value.serialize(serializer)
            }
            Self::Err(error) => {
                serializer.write_enum_variant(1)?;
                error.serialize(serializer)
            }
        }
    }
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for NetConfigResult<'de, T> {
    fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
        match deserializer.read_enum_variant()? {
            0 => T::deserialize(deserializer).map(Self::Ok),
            1 => <&str>::deserialize(deserializer).map(Self::Err),
            _ => Err(D::Error::INVALID_ENUM_VARIANT),
        }
    }
}
