use std::io;

use base64::Engine;
use base64::engine::general_purpose::STANDARD_NO_PAD;
use serde::Deserialize;

use crate::data_model::resources::as_resources::Asn;
use crate::rtr::payload::Ski;
use crate::slurm::file::{SlurmError, SlurmFile, SlurmVersion};
use crate::slurm::policy::{
    AspaAssertion, AspaFilter, BgpsecAssertion, BgpsecFilter, LocallyAddedAssertions,
    PrefixAssertion, PrefixFilter, ValidationOutputFilters, parse_ip_prefix,
};

impl SlurmFile {
    pub fn from_slice(input: &[u8]) -> Result<Self, SlurmError> {
        let version = serde_json::from_slice::<SlurmVersionMarker>(input)?.slurm_version;
        match version {
            SlurmVersion::V1_U32 => {
                let raw = serde_json::from_slice::<RawSlurmFileV1>(input)?;
                Self::from_raw_v1(raw)
            }
            SlurmVersion::V2_U32 => {
                let raw = serde_json::from_slice::<RawSlurmFileV2>(input)?;
                Self::from_raw_v2(raw)
            }
            other => Err(SlurmError::Invalid(format!(
                "unsupported slurmVersion {}, expected 1 or 2",
                other
            ))),
        }
    }

    pub fn from_reader(mut reader: impl io::Read) -> Result<Self, SlurmError> {
        let mut bytes = Vec::new();
        reader.read_to_end(&mut bytes)?;
        Self::from_slice(&bytes)
    }

    fn from_raw_v1(raw: RawSlurmFileV1) -> Result<Self, SlurmError> {
        Self::new(
            SlurmVersion::V1,
            ValidationOutputFilters {
                prefix_filters: raw.validation_output_filters.prefix_filters,
                bgpsec_filters: raw.validation_output_filters.bgpsec_filters,
                aspa_filters: Vec::new(),
            },
            LocallyAddedAssertions {
                prefix_assertions: raw.locally_added_assertions.prefix_assertions,
                bgpsec_assertions: raw.locally_added_assertions.bgpsec_assertions,
                aspa_assertions: Vec::new(),
            },
        )
    }

    fn from_raw_v2(raw: RawSlurmFileV2) -> Result<Self, SlurmError> {
        Self::new(
            SlurmVersion::V2,
            ValidationOutputFilters {
                prefix_filters: raw.validation_output_filters.prefix_filters,
                bgpsec_filters: raw.validation_output_filters.bgpsec_filters,
                aspa_filters: raw.validation_output_filters.aspa_filters,
            },
            LocallyAddedAssertions {
                prefix_assertions: raw.locally_added_assertions.prefix_assertions,
                bgpsec_assertions: raw.locally_added_assertions.bgpsec_assertions,
                aspa_assertions: raw.locally_added_assertions.aspa_assertions,
            },
        )
    }
}

#[derive(Deserialize)]
struct SlurmVersionMarker {
    #[serde(rename = "slurmVersion")]
    slurm_version: u32,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawSlurmFileV1 {
    #[serde(rename = "slurmVersion")]
    _slurm_version: u32,
    #[serde(rename = "validationOutputFilters")]
    validation_output_filters: RawValidationOutputFiltersV1,
    #[serde(rename = "locallyAddedAssertions")]
    locally_added_assertions: RawLocallyAddedAssertionsV1,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawSlurmFileV2 {
    #[serde(rename = "slurmVersion")]
    _slurm_version: u32,
    #[serde(rename = "validationOutputFilters")]
    validation_output_filters: RawValidationOutputFiltersV2,
    #[serde(rename = "locallyAddedAssertions")]
    locally_added_assertions: RawLocallyAddedAssertionsV2,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawValidationOutputFiltersV1 {
    #[serde(rename = "prefixFilters")]
    prefix_filters: Vec<PrefixFilter>,
    #[serde(rename = "bgpsecFilters")]
    bgpsec_filters: Vec<BgpsecFilter>,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawValidationOutputFiltersV2 {
    #[serde(rename = "prefixFilters")]
    prefix_filters: Vec<PrefixFilter>,
    #[serde(rename = "bgpsecFilters")]
    bgpsec_filters: Vec<BgpsecFilter>,
    #[serde(rename = "aspaFilters")]
    aspa_filters: Vec<AspaFilter>,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawLocallyAddedAssertionsV1 {
    #[serde(rename = "prefixAssertions")]
    prefix_assertions: Vec<PrefixAssertion>,
    #[serde(rename = "bgpsecAssertions")]
    bgpsec_assertions: Vec<BgpsecAssertion>,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawLocallyAddedAssertionsV2 {
    #[serde(rename = "prefixAssertions")]
    prefix_assertions: Vec<PrefixAssertion>,
    #[serde(rename = "bgpsecAssertions")]
    bgpsec_assertions: Vec<BgpsecAssertion>,
    #[serde(rename = "aspaAssertions")]
    aspa_assertions: Vec<AspaAssertion>,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawPrefixFilter {
    prefix: Option<String>,
    asn: Option<u32>,
    comment: Option<String>,
}

impl<'de> Deserialize<'de> for PrefixFilter {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let raw = RawPrefixFilter::deserialize(deserializer)?;
        Ok(Self {
            prefix: raw
                .prefix
                .map(|prefix| parse_ip_prefix(&prefix))
                .transpose()
                .map_err(serde::de::Error::custom)?,
            asn: raw.asn.map(Asn::from),
            comment: raw.comment,
        })
    }
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawBgpsecFilter {
    asn: Option<u32>,
    #[serde(rename = "SKI")]
    ski: Option<String>,
    comment: Option<String>,
}

impl<'de> Deserialize<'de> for BgpsecFilter {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let raw = RawBgpsecFilter::deserialize(deserializer)?;
        Ok(Self {
            asn: raw.asn.map(Asn::from),
            ski: raw
                .ski
                .map(|ski| decode_ski(&ski))
                .transpose()
                .map_err(serde::de::Error::custom)?,
            comment: raw.comment,
        })
    }
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawAspaFilter {
    #[serde(rename = "customerAsn")]
    customer_asn: u32,
    comment: Option<String>,
}

impl<'de> Deserialize<'de> for AspaFilter {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let raw = RawAspaFilter::deserialize(deserializer)?;
        Ok(Self {
            customer_asn: Asn::from(raw.customer_asn),
            comment: raw.comment,
        })
    }
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawPrefixAssertion {
    prefix: String,
    asn: u32,
    #[serde(rename = "maxPrefixLength")]
    max_prefix_length: Option<u8>,
    comment: Option<String>,
}

impl<'de> Deserialize<'de> for PrefixAssertion {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let raw = RawPrefixAssertion::deserialize(deserializer)?;
        Ok(Self {
            prefix: parse_ip_prefix(&raw.prefix).map_err(serde::de::Error::custom)?,
            asn: Asn::from(raw.asn),
            max_prefix_length: raw.max_prefix_length,
            comment: raw.comment,
        })
    }
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawBgpsecAssertion {
    asn: u32,
    #[serde(rename = "SKI")]
    ski: String,
    #[serde(rename = "routerPublicKey")]
    router_public_key: String,
    comment: Option<String>,
}

impl<'de> Deserialize<'de> for BgpsecAssertion {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let raw = RawBgpsecAssertion::deserialize(deserializer)?;
        Ok(Self {
            asn: Asn::from(raw.asn),
            ski: decode_ski(&raw.ski).map_err(serde::de::Error::custom)?,
            router_public_key: decode_router_public_key(&raw.router_public_key)
                .map_err(serde::de::Error::custom)?,
            comment: raw.comment,
        })
    }
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct RawAspaAssertion {
    #[serde(rename = "customerAsn")]
    customer_asn: u32,
    #[serde(rename = "providerAsns")]
    provider_asns: Vec<u32>,
    comment: Option<String>,
}

impl<'de> Deserialize<'de> for AspaAssertion {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let raw = RawAspaAssertion::deserialize(deserializer)?;
        Ok(Self {
            customer_asn: Asn::from(raw.customer_asn),
            provider_asns: raw.provider_asns.into_iter().map(Asn::from).collect(),
            comment: raw.comment,
        })
    }
}

fn decode_ski(input: &str) -> Result<Ski, SlurmError> {
    let bytes = STANDARD_NO_PAD
        .decode(input)
        .map_err(|err| SlurmError::Invalid(format!("invalid SKI base64 '{}': {}", input, err)))?;
    if bytes.len() != 20 {
        return Err(SlurmError::Invalid(format!(
            "SKI must be exactly 20 bytes, got {}",
            bytes.len()
        )));
    }

    let mut ski = [0u8; 20];
    ski.copy_from_slice(&bytes);
    Ok(Ski::from_bytes(ski))
}

fn decode_router_public_key(input: &str) -> Result<Vec<u8>, SlurmError> {
    STANDARD_NO_PAD.decode(input).map_err(|err| {
        SlurmError::Invalid(format!(
            "invalid routerPublicKey base64 '{}': {}",
            input, err
        ))
    })
}
