use build_html::{self, Html, HtmlContainer};

#[derive(Clone, Debug, Default)]
pub struct Attributes(Vec<(String, String)>);

/*
impl FromIterator<(String, String)> for Attributes {
    fn from_iter<T>(iter: T) -> Self
    where
        T: IntoIterator<Item = (String, String)>,
    {
        Attributes(iter.collect::<Vec<(String, String)>>())
    }
}

impl FromIterator<(&str, &str)> for Attributes {
    fn from_iter<T>(iter: T) -> Self
    where
        T: IntoIterator<Item = (&str, &str)>,
    {
        unimplemented!()
    }
}
*/

impl ToString for Attributes {
    fn to_string(&self) -> String {
        self.0
            .iter()
            .map(|(key, value)| format!("{}=\"{}\"", key, value))
            .collect::<Vec<String>>()
            .join(" ")
    }
}

#[derive(Clone, Debug)]
pub struct Form {
    path: String,
    method: String,
    encoding: Option<String>,
    elements: String,
}

impl Form {
    pub fn new() -> Self {
        Self {
            path: "/".to_owned(),
            method: "get".to_owned(),
            encoding: None,
            elements: "".to_owned(),
        }
    }

    pub fn with_path(mut self, path: &str) -> Self {
        self.path = path.to_owned();
        self
    }

    pub fn with_method(mut self, method: &str) -> Self {
        self.method = method.to_owned();
        self
    }

    pub fn with_encoding(mut self, encoding: &str) -> Self {
        self.encoding = Some(encoding.to_owned());
        self
    }
}

impl Html for Form {
    fn to_html_string(&self) -> String {
        let encoding = match self.encoding {
            Some(ref encoding) => format!("enctype=\"{encoding}\"", encoding = encoding),
            None => "".to_owned(),
        };
        format!(
            "<form action=\"{path}\" method=\"{method}\" {encoding}>\n{elements}\n</form>\n",
            path = self.path,
            method = self.method,
            encoding = encoding,
            elements = self.elements.to_html_string(),
        )
    }
}

impl HtmlContainer for Form {
    fn add_html<H: Html>(&mut self, html: H) {
        self.elements.push_str(&html.to_html_string());
    }
}

#[derive(Clone, Debug)]
pub struct Input {
    ty: String,
    name: String,
    id: Option<String>,
    value: Option<String>,
    attributes: Attributes,
}

impl Html for Input {
    fn to_html_string(&self) -> String {
        let id = match self.id {
            Some(ref id) => format!("id=\"{}\"", id),
            None => "".to_owned(),
        };
        let value = match self.value {
            Some(ref value) => format!("value=\"{}\"", value),
            None => "".to_owned(),
        };
        let attrs = self.attributes.to_string();

        format!(
            "<input type=\"{ty}\" name=\"{name}\" {id} {value} {attrs} />\n",
            ty = self.ty,
            name = self.name,
            id = id,
            value = value,
            attrs = attrs,
        )
    }
}

impl Input {
    pub fn new(ty: &str, name: &str) -> Self {
        Self {
            ty: ty.to_owned(),
            name: name.to_owned(),
            id: None,
            value: None,
            attributes: Attributes::default(),
        }
    }

    pub fn with_id(mut self, val: &str) -> Self {
        self.id = Some(val.to_owned());
        self
    }

    pub fn with_attributes<'a>(
        mut self,
        values: impl IntoIterator<Item = (&'a str, &'a str)>,
    ) -> Self {
        self.attributes = Attributes(
            values
                .into_iter()
                .map(|(a, b)| (a.to_owned(), b.to_owned()))
                .collect::<Vec<(String, String)>>(),
        );
        self
    }
}

#[derive(Clone, Debug)]
pub struct Button {
    ty: Option<String>,
    name: Option<String>,
    label: String,
    attributes: Attributes,
}

impl Button {
    pub fn new(label: &str) -> Self {
        Self {
            ty: None,
            name: None,
            label: label.to_owned(),
            attributes: Attributes::default(),
        }
    }

    pub fn with_type(mut self, ty: &str) -> Self {
        self.ty = Some(ty.to_owned());
        self
    }

    pub fn with_attributes<'a>(
        mut self,
        values: impl IntoIterator<Item = (&'a str, &'a str)>,
    ) -> Self {
        self.attributes = Attributes(
            values
                .into_iter()
                .map(|(a, b)| (a.to_owned(), b.to_owned()))
                .collect::<Vec<(String, String)>>(),
        );
        self
    }
}

impl Html for Button {
    fn to_html_string(&self) -> String {
        let ty = match self.ty {
            Some(ref ty) => format!("type={}", ty),
            None => "".to_owned(),
        };
        let name = match self.name {
            Some(ref name) => format!("name={}", name),
            None => "".to_owned(),
        };
        format!(
            "<button {ty} {name} {attrs}>{label}</button>",
            name = name,
            label = self.label,
            attrs = self.attributes.to_string()
        )
    }
}
