use url::Url;
use x509_parser::prelude::{FromDer, X509Certificate};

use crate::data_model::oid::OID_CP_IPADDR_ASNUMBER;
use crate::data_model::rc::{
    AsIdentifierChoice, IpAddressChoice, ResourceCertKind, ResourceCertificate,
    ResourceCertificateParseError, ResourceCertificateParsed, ResourceCertificateProfileError,
    ResourceCertificateRole,
};
use crate::data_model::tal::Tal;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TaCertificate {
    pub raw_der: Vec<u8>,
    pub rc_ca: ResourceCertificate,
}

#[derive(Debug, thiserror::Error)]
pub enum TaCertificateParseError {
    #[error("TA certificate parse error: {0} (RFC 5280 §4.1; RFC 6487 §4; RFC 8630 §2.3)")]
    ResourceCertificate(#[from] ResourceCertificateParseError),
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TaCertificateParsed {
    pub rc_parsed: ResourceCertificateParsed,
}

#[derive(Debug, thiserror::Error)]
pub enum TaCertificateProfileError {
    #[error("resource certificate profile error: {0} (RFC 5280 §4; RFC 6487 §4)")]
    ResourceCertificate(#[from] ResourceCertificateProfileError),

    #[error("TA certificate must be a CA certificate (RFC 8630 §2.3; RFC 6487 §4.8.1)")]
    NotCa,

    #[error(
        "TA certificate must be self-signed (issuer DN must equal subject DN) (RFC 8630 §2.3; RFC 5280 §4.1.2.4)"
    )]
    NotSelfSignedIssuerSubject,

    #[error(
        "TA certificate must contain certificatePolicies ipAddr-asNumber ({OID_CP_IPADDR_ASNUMBER}) (RFC 6487 §4.8.9; RFC 8630 §2.3)"
    )]
    MissingOrInvalidCertificatePolicies,

    #[error("TA certificate must contain SubjectKeyIdentifier (RFC 6487 §4.8.2; RFC 8630 §2.3)")]
    MissingSubjectKeyIdentifier,

    #[error(
        "TA certificate must contain at least one RFC 3779 resource extension (IP or AS) (RFC 6487 §4.8.10-§4.8.11; RFC 8630 §2.3)"
    )]
    ResourcesMissing,

    #[error("TA certificate resources must be non-empty (RFC 8630 §2.3)")]
    ResourcesEmpty,

    #[error(
        "TA certificate MUST NOT use inherit in IP resources (RFC 8630 §2.3; RFC 3779 §2.2.3.5)"
    )]
    IpResourcesInherit,

    #[error(
        "TA certificate MUST NOT use inherit in AS resources (RFC 8630 §2.3; RFC 3779 §3.2.3.3)"
    )]
    AsResourcesInherit,
}

#[derive(Debug, thiserror::Error)]
pub enum TaCertificateDecodeError {
    #[error("{0}")]
    Parse(#[from] TaCertificateParseError),

    #[error("{0}")]
    Validate(#[from] TaCertificateProfileError),
}

/// Backwards-compatible name: TA certificate errors from parse+validate.
pub type TaCertificateError = TaCertificateDecodeError;

#[derive(Debug, thiserror::Error)]
pub enum TaCertificateVerifyError {
    #[error("TA certificate parse error: {0} (RFC 5280 §4.1; RFC 8630 §2.3)")]
    Parse(String),

    #[error("trailing bytes after TA certificate DER: {0} bytes (DER; RFC 5280 §4.1)")]
    TrailingBytes(usize),

    #[error(
        "TA certificate self-signature verification failed: {0} (RFC 8630 §2.3; RFC 5280 §6.1)"
    )]
    InvalidSelfSignature(String),
}

impl TaCertificate {
    /// Parse step of scheme A (`parse → validate → verify`).
    pub fn parse_der(der: &[u8]) -> Result<TaCertificateParsed, TaCertificateParseError> {
        Ok(TaCertificateParsed {
            rc_parsed: ResourceCertificate::parse_der(der)?,
        })
    }

    /// Profile validate step of scheme A (`parse → validate → verify`).
    ///
    /// `TaCertificate` is already profile-validated when constructed via `decode_der()` /
    /// `TaCertificateParsed::validate_profile()`.
    pub fn validate_profile(&self) -> Result<(), TaCertificateProfileError> {
        Ok(())
    }

    /// Decode a TA certificate (`parse + validate`).
    pub fn decode_der(der: &[u8]) -> Result<Self, TaCertificateDecodeError> {
        Ok(Self::parse_der(der)?.validate_profile()?)
    }

    pub fn decode_der_with_strict_name(der: &[u8]) -> Result<Self, TaCertificateDecodeError> {
        let ta = Self::decode_der(der)?;
        ta.rc_ca
            .validate_strict_name_profile()
            .map_err(TaCertificateProfileError::from)?;
        Ok(ta)
    }

    /// Backwards-compatible helper (historical name).
    pub fn from_der(der: &[u8]) -> Result<Self, TaCertificateError> {
        Self::decode_der(der)
    }

    pub fn spki_der(&self) -> &[u8] {
        &self.rc_ca.tbs.subject_public_key_info
    }

    /// Verify step of scheme A (`parse → validate → verify`).
    pub fn verify_self_signature(&self) -> Result<(), TaCertificateVerifyError> {
        let (rem, cert) = X509Certificate::from_der(&self.raw_der)
            .map_err(|e| TaCertificateVerifyError::Parse(e.to_string()))?;
        if !rem.is_empty() {
            return Err(TaCertificateVerifyError::TrailingBytes(rem.len()));
        }
        cert.verify_signature(None)
            .map_err(|e| TaCertificateVerifyError::InvalidSelfSignature(e.to_string()))?;
        Ok(())
    }

    /// Validate TA-specific semantic constraints on a parsed Resource Certificate.
    ///
    /// Note: this does not verify the X.509 signature; it is intended for higher-level logic and
    /// for unit tests that exercise individual constraint branches.
    pub fn validate_rc_constraints(
        rc_ca: &ResourceCertificate,
    ) -> Result<(), TaCertificateProfileError> {
        if rc_ca.kind != ResourceCertKind::Ca {
            return Err(TaCertificateProfileError::NotCa);
        }

        if rc_ca.tbs.extensions.certificate_policies_oid.as_deref() != Some(OID_CP_IPADDR_ASNUMBER)
        {
            return Err(TaCertificateProfileError::MissingOrInvalidCertificatePolicies);
        }
        if rc_ca.tbs.extensions.subject_key_identifier.is_none() {
            return Err(TaCertificateProfileError::MissingSubjectKeyIdentifier);
        }

        let ip = rc_ca.tbs.extensions.ip_resources.as_ref();
        let asn = rc_ca.tbs.extensions.as_resources.as_ref();
        if ip.is_none() && asn.is_none() {
            return Err(TaCertificateProfileError::ResourcesMissing);
        }

        let mut has_any_resource = false;

        if let Some(ip) = ip {
            if ip.has_any_inherit() {
                return Err(TaCertificateProfileError::IpResourcesInherit);
            }
            for fam in &ip.families {
                match &fam.choice {
                    IpAddressChoice::Inherit => {
                        return Err(TaCertificateProfileError::IpResourcesInherit);
                    }
                    IpAddressChoice::AddressesOrRanges(items) => {
                        if !items.is_empty() {
                            has_any_resource = true;
                        }
                    }
                }
            }
        }

        if let Some(asn) = asn {
            if matches!(asn.asnum, Some(AsIdentifierChoice::Inherit))
                || matches!(asn.rdi, Some(AsIdentifierChoice::Inherit))
            {
                return Err(TaCertificateProfileError::AsResourcesInherit);
            }
            if let Some(AsIdentifierChoice::AsIdsOrRanges(items)) = asn.asnum.as_ref() {
                if !items.is_empty() {
                    has_any_resource = true;
                }
            }
            if let Some(AsIdentifierChoice::AsIdsOrRanges(items)) = asn.rdi.as_ref() {
                if !items.is_empty() {
                    has_any_resource = true;
                }
            }
        }

        if !has_any_resource {
            return Err(TaCertificateProfileError::ResourcesEmpty);
        }

        Ok(())
    }
}

impl TaCertificateParsed {
    pub fn validate_profile(self) -> Result<TaCertificate, TaCertificateProfileError> {
        let rc_ca = self.rc_parsed.validate_profile()?;
        if rc_ca.kind != ResourceCertKind::Ca {
            return Err(TaCertificateProfileError::NotCa);
        }
        rc_ca.validate_rfc6487_profile(ResourceCertificateRole::TrustAnchor)?;

        if rc_ca.tbs.issuer_name != rc_ca.tbs.subject_name {
            return Err(TaCertificateProfileError::NotSelfSignedIssuerSubject);
        }

        TaCertificate::validate_rc_constraints(&rc_ca)?;

        Ok(TaCertificate {
            raw_der: rc_ca.raw_der.clone(),
            rc_ca,
        })
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TrustAnchor {
    pub tal: Tal,
    pub ta_certificate: TaCertificate,
    pub resolved_ta_uri: Option<Url>,
}

#[derive(Debug, thiserror::Error)]
pub enum TrustAnchorError {
    #[error("TA certificate error: {0} (RFC 8630 §2.3)")]
    TaCertificate(#[from] TaCertificateDecodeError),

    #[error("TA certificate self-signature error: {0} (RFC 8630 §2.3)")]
    TaSelfSignature(#[from] TaCertificateVerifyError),

    #[error("{0}")]
    Bind(#[from] TrustAnchorBindError),
}

#[derive(Debug, thiserror::Error)]
pub enum TrustAnchorBindError {
    #[error("resolved TA URI not listed in TAL: {0} (RFC 8630 §2.2-§2.3)")]
    ResolvedUriNotInTal(String),

    #[error(
        "TAL SPKI does not match TA certificate SubjectPublicKeyInfo (RFC 8630 §2.3; RFC 5280 §4.1.2.7)"
    )]
    TalSpkiMismatch,
}

impl TrustAnchor {
    /// Bind a TAL and a downloaded TA certificate.
    ///
    /// This does not download anything; it only validates the binding rules from RFC 8630 §2.3.
    pub fn bind_der(
        tal: Tal,
        ta_der: &[u8],
        resolved_uri: Option<&Url>,
    ) -> Result<Self, TrustAnchorError> {
        let ta_certificate = TaCertificate::decode_der(ta_der)?;
        ta_certificate.verify_self_signature()?;
        Ok(Self::bind(tal, ta_certificate, resolved_uri)?)
    }

    pub fn bind_der_with_strict_name(
        tal: Tal,
        ta_der: &[u8],
        resolved_uri: Option<&Url>,
    ) -> Result<Self, TrustAnchorError> {
        let ta_certificate = TaCertificate::decode_der_with_strict_name(ta_der)?;
        ta_certificate.verify_self_signature()?;
        Ok(Self::bind(tal, ta_certificate, resolved_uri)?)
    }

    pub fn bind(
        tal: Tal,
        ta_certificate: TaCertificate,
        resolved_uri: Option<&Url>,
    ) -> Result<Self, TrustAnchorBindError> {
        if let Some(u) = resolved_uri {
            if !tal.ta_uris.iter().any(|x| x == u) {
                return Err(TrustAnchorBindError::ResolvedUriNotInTal(u.to_string()));
            }
        }

        if tal.subject_public_key_info_der != ta_certificate.spki_der() {
            return Err(TrustAnchorBindError::TalSpkiMismatch);
        }

        Ok(Self {
            tal,
            ta_certificate,
            resolved_ta_uri: resolved_uri.cloned(),
        })
    }
}
