use crate::data_model::resources::as_resources::Asn;
use crate::data_model::resources::ip_resources::IPAddressPrefix;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::io;
use std::time::Duration;
use x509_parser::prelude::FromDer;
use x509_parser::x509::SubjectPublicKeyInfo;

#[derive(
    Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd, Serialize, Deserialize,
)]
pub struct Ski([u8; 20]);

impl AsRef<[u8]> for Ski {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Ski {
    pub fn from_bytes(bytes: [u8; 20]) -> Self {
        Self(bytes)
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct RouteOrigin {
    prefix: IPAddressPrefix,
    max_length: u8,
    asn: Asn,
}

impl RouteOrigin {
    pub fn new(prefix: IPAddressPrefix, max_length: u8, asn: Asn) -> Self {
        Self {
            prefix,
            max_length,
            asn,
        }
    }

    pub fn prefix(&self) -> &IPAddressPrefix {
        &self.prefix
    }

    pub fn max_length(&self) -> u8 {
        self.max_length
    }

    pub fn asn(&self) -> Asn {
        self.asn
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct RouterKey {
    subject_key_identifier: Ski,
    asn: Asn,
    subject_public_key_info: Vec<u8>,
}

impl RouterKey {
    pub fn new(subject_key_identifier: Ski, asn: Asn, subject_public_key_info: Vec<u8>) -> Self {
        Self {
            subject_key_identifier,
            asn,
            subject_public_key_info,
        }
    }

    pub fn ski(&self) -> Ski {
        self.subject_key_identifier
    }

    pub fn asn(&self) -> Asn {
        self.asn
    }

    pub fn spki(&self) -> &[u8] {
        &self.subject_public_key_info
    }

    pub fn validate(&self) -> Result<(), io::Error> {
        if self.asn.into_u32() == 0 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "RouterKey ASN must not be AS0",
            ));
        }

        if self.subject_public_key_info.is_empty() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "RouterKey SPKI must not be empty",
            ));
        }

        let (rem, _) =
            SubjectPublicKeyInfo::from_der(&self.subject_public_key_info).map_err(|err| {
                io::Error::new(
                    io::ErrorKind::InvalidData,
                    format!("RouterKey SPKI is not valid DER: {err}"),
                )
            })?;

        if !rem.is_empty() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!("RouterKey SPKI DER has trailing bytes: {}", rem.len()),
            ));
        }

        Ok(())
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Aspa {
    customer_asn: Asn,
    provider_asns: Vec<Asn>,
}

impl Aspa {
    pub fn new(customer_asn: Asn, mut provider_asns: Vec<Asn>) -> Self {
        provider_asns.sort();
        provider_asns.dedup();

        Self {
            customer_asn,
            provider_asns,
        }
    }

    pub fn customer_asn(&self) -> Asn {
        self.customer_asn
    }

    pub fn provider_asns(&self) -> &[Asn] {
        &self.provider_asns
    }

    pub fn validate_announcement(&self) -> Result<(), io::Error> {
        if self.customer_asn.into_u32() == 0 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "ASPA customer ASN must not be AS0",
            ));
        }

        if self.provider_asns.is_empty() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "ASPA announcement must contain at least one provider ASN",
            ));
        }

        if self.provider_asns.iter().any(|asn| asn.into_u32() == 0)
            && !(self.provider_asns.len() == 1 && self.provider_asns[0].into_u32() == 0)
        {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "ASPA provider list containing AS0 must be exactly [0]",
            ));
        }

        Ok(())
    }
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum Payload {
    /// A route origin.
    RouteOrigin(RouteOrigin),

    /// A BGPsec router key.
    RouterKey(RouterKey),

    /// An ASPA unit.
    Aspa(Aspa),
}

// Timing
#[derive(Clone, Copy, Debug)]
pub struct Timing {
    /// The number of seconds until a client should refresh its data.
    pub refresh: u32,

    /// The number of seconds a client whould wait before retrying to connect.
    pub retry: u32,

    /// The number of secionds before data expires if not refreshed.
    pub expire: u32,
}

impl Timing {
    pub const MIN_REFRESH: u32 = 1;
    pub const MAX_REFRESH: u32 = 86_400;
    pub const MIN_RETRY: u32 = 1;
    pub const MAX_RETRY: u32 = 7_200;
    pub const MIN_EXPIRE: u32 = 600;
    pub const MAX_EXPIRE: u32 = 172_800;

    pub const fn new(refresh: u32, retry: u32, expire: u32) -> Self {
        Self {
            refresh,
            retry,
            expire,
        }
    }

    pub fn validate(self) -> Result<(), io::Error> {
        if !(Self::MIN_REFRESH..=Self::MAX_REFRESH).contains(&self.refresh) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "refresh interval {} out of range {}..={}",
                    self.refresh,
                    Self::MIN_REFRESH,
                    Self::MAX_REFRESH
                ),
            ));
        }

        if !(Self::MIN_RETRY..=Self::MAX_RETRY).contains(&self.retry) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "retry interval {} out of range {}..={}",
                    self.retry,
                    Self::MIN_RETRY,
                    Self::MAX_RETRY
                ),
            ));
        }

        if !(Self::MIN_EXPIRE..=Self::MAX_EXPIRE).contains(&self.expire) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "expire interval {} out of range {}..={}",
                    self.expire,
                    Self::MIN_EXPIRE,
                    Self::MAX_EXPIRE
                ),
            ));
        }

        if self.expire <= self.refresh {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "expire interval {} must be greater than refresh interval {}",
                    self.expire, self.refresh
                ),
            ));
        }

        if self.expire <= self.retry {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "expire interval {} must be greater than retry interval {}",
                    self.expire, self.retry
                ),
            ));
        }

        Ok(())
    }

    pub fn refresh(self) -> Duration {
        Duration::from_secs(u64::from(self.refresh))
    }

    pub fn retry(self) -> Duration {
        Duration::from_secs(u64::from(self.retry))
    }

    pub fn expire(self) -> Duration {
        Duration::from_secs(u64::from(self.expire))
    }
}

impl Default for Timing {
    fn default() -> Self {
        Self {
            refresh: 3600,
            retry: 600,
            expire: 7200,
        }
    }
}
