use super::{
    class::{Class, EmptyVector},
    id::Id,
    main::Main,
    Script,
};
use crate::{
    attributes::{Attribute, Value},
    node::{Element, Node},
    text::Text,
};
use std::convert::TryInto;

pub struct Body {
    pub content: Vec<BodyElement>,
    pub scripts: Vec<Script>,
    pub id: Option<Id>,
    pub class: Vec<Class>,
}

pub enum BodyElement {
    Text(Text),
    Main(Main),
}

impl<'a> From<BodyElement> for Node<'a> {
    fn from(value: BodyElement) -> Self {
        match value {
            BodyElement::Text(text) => text.into(),
            BodyElement::Main(main) => main.into(),
        }
    }
}

impl<'a> From<Text> for BodyElement {
    fn from(value: Text) -> Self {
        BodyElement::Text(value)
    }
}

impl<'a> From<Body> for Element<'a, Vec<Node<'a>>> {
    fn from(value: Body) -> Self {
        let empty: Vec<Node> = vec![];
        let mut body: Element<Vec<Node>> = Element::body(empty);

        match value.id {
            Some(id) => body.set_attribute(Attribute::ID, id.into()),
            None => (),
        };

        let class_value: Result<Value<'a>, EmptyVector> = value.class.try_into();
        match class_value {
            Ok(value) => body.set_attribute(Attribute::CLASS, value),
            Err(_) => {}
        }

        for item in value.content {
            let el: Node = item.into();
            body.push(el);
        }

        for script in value.scripts {
            let el: Node = script.into();
            body.push(el);
        }
        body
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::attributes::{Attribute, Value};

    #[test]
    fn preserves_id() {
        let id = Id::create("abc").unwrap();
        let id = Some(id);
        let body = Body {
            content: vec![],
            scripts: vec![],
            id,
            class: vec![],
        };
        let el: Element<Vec<Node>> = body.into();
        let result = el.has_attribute(&Attribute::ID);
        assert!(result);
    }

    #[test]
    fn preserves_class() {
        let class = Class::create("abc").unwrap();
        let body = Body {
            content: vec![],
            scripts: vec![],
            id: None,
            class: vec![class],
        };
        let el: Element<Vec<Node>> = body.into();
        let result = el.get_attribute_value(&Attribute::CLASS);
        let expected = {
            let value = Value::owned("abc").unwrap();
            &Some(value)
        };
        assert_eq!(result, expected);
    }
}
