use magnetar_core::web_model::{content_type::ContentXrdXml, rel::RelLrdd, Rel};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum XrdXml {
    #[serde(rename = "XRD")]
    Xrd(Xrd),
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Xrd {
    #[serde(rename = "@xmlns")]
    xmlns: String,
    #[serde(rename = "Link")]
    links: Vec<Link>,
}

impl Xrd {
    pub fn default_host_meta(protocol: &str, domain: &str) -> Xrd {
        Xrd {
            xmlns: r#"http://docs.oasis-open.org/ns/xri/xrd-1.0"#.to_string(),
            links: vec![Link {
                rel: Some(RelLrdd.rel().to_string()),
                r#type: Some(ContentXrdXml.as_ref().to_string()),
                href: None,
                template: Some(format!(
                    "{}://{}/.well-known/webfinger?resource={{uri}}",
                    protocol, domain
                )),
            }],
        }
    }

    pub fn get_webfinger_template(&self) -> Option<&str> {
        self.links
            .iter()
            .find(|l| {
                l.r#type.as_deref() == Some(ContentXrdXml.as_ref())
                    && l.rel.as_deref() == Some(RelLrdd.as_ref())
            })
            .and_then(|l| l.template.as_deref())
    }
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename = "Link")]
pub struct Link {
    #[serde(rename = "@rel")]
    #[serde(skip_serializing_if = "Option::is_none")]
    rel: Option<String>,
    #[serde(rename = "@type")]
    #[serde(skip_serializing_if = "Option::is_none")]
    r#type: Option<String>,
    #[serde(rename = "@href")]
    #[serde(skip_serializing_if = "Option::is_none")]
    href: Option<String>,
    #[serde(rename = "@template")]
    #[serde(skip_serializing_if = "Option::is_none")]
    template: Option<String>,
}

#[cfg(test)]
mod test {
    use std::error::Error;

    use crate::{Xrd, XrdXml};

    #[test]
    fn should_parse_host_meta() -> Result<(), Box<dyn Error>> {
        let xml = r#"
            <?xml version="1.0" encoding="UTF-8"?>
            <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
                <Link rel="lrdd" type="application/xrd+xml" template="https://example.org/.well-known/webfinger?resource={uri}"/>
            </XRD>
        "#;

        let XrdXml::Xrd(xrd) = quick_xml::de::from_str::<XrdXml>(xml)?;

        assert_eq!(xrd, Xrd::default_host_meta("https", "example.org"));

        Ok(())
    }

    #[test]
    fn should_fail_on_invalid_root_tag() {
        let xml = r#"
            <?xml version="1.0" encoding="UTF-8"?>
            <ABC xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
                <Link rel="lrdd" type="application/xrd+xml" template="https://example.org/.well-known/webfinger?resource={uri}"/>
            </ABC>
        "#;

        let xrd = quick_xml::de::from_str::<XrdXml>(xml);

        assert!(xrd.is_err());
    }
}
