mod base16;
mod tinted8;

use crate::{error::TintedBuilderError, scheme::Scheme};

/// A struct representing a template that can be rendered with the provided color scheme.
///
/// The `Template` struct holds the content of the template and the scheme used to render it. It
/// provides methods to create a new template and render it into a `String` using the specified
/// color scheme.
pub struct Template {
    content: String,
    scheme: Scheme,
}

impl Template {
    /// Creates a new `Template` instance.
    ///
    /// # Arguments
    ///
    /// * `content` - A `String` representing the content of the template.
    /// * `scheme` - A `Scheme` enum that determines which color scheme to use when rendering the template.
    ///
    /// # Returns
    ///
    /// A new `Template` instance with the provided content and scheme.
    #[must_use]
    pub const fn new(content: String, scheme: Scheme) -> Self {
        Self { content, scheme }
    }

    /// Renders the template into a `String` using the provided color scheme.
    ///
    /// This method applies the specified `Scheme` to the template content, converting placeholders
    /// in the content to their corresponding values from the scheme context.
    ///
    /// # Errors
    ///
    /// Returns a `TintedBuilderError` if the rendering process fails. This could happen if the
    /// content contains placeholders that cannot be resolved using the scheme context.
    ///
    /// # Examples
    ///
    /// ```
    /// use tinted_builder::{Template, Scheme};
    ///
    /// let scheme_yaml = r#"
    /// system: "base16"
    /// name: "Some name"
    /// author: "Some author"
    /// variant: "dark"
    /// palette:
    ///   base00: "241b26"
    ///   base01: "2f2a3f"
    ///   base02: "46354a"
    ///   base03: "6c3cb2"
    ///   base04: "7e5f83"
    ///   base05: "eed5d9"
    ///   base06: "d9c2c6"
    ///   base07: "e4ccd0"
    ///   base08: "877bb6"
    ///   base09: "de5b44"
    ///   base0A: "a84a73"
    ///   base0B: "c965bf"
    ///   base0C: "9c5fce"
    ///   base0D: "6a9eb5"
    ///   base0E: "78a38f"
    ///   base0F: "a3a079"
    /// "#;
    /// let template = Template::new(
    ///     r#"{{scheme-system}} scheme name is "{{scheme-name}}" and first color is #{{base00-hex}}"#.to_string(),
    ///     Scheme::from_yaml(scheme_yaml).unwrap()
    /// );
    /// let rendered = template.render().unwrap();
    ///
    /// assert_eq!(
    ///     rendered,
    ///     r#"base16 scheme name is "Some name" and first color is #241b26"#
    /// );
    /// ```
    pub fn render(&self) -> Result<String, TintedBuilderError> {
        match self.scheme {
            Scheme::Base16(ref scheme) => {
                let ctx = base16::to_template_context(&scheme.into());
                let rendered = base16::render(&self.content, &ctx)?;
                Ok(rendered)
            }
            Scheme::Base24(ref scheme) => {
                let ctx = base16::to_template_context(&scheme.into());
                let rendered = base16::render(&self.content, &ctx)?;
                Ok(rendered)
            }
            Scheme::Tinted8(ref scheme) => {
                let ctx = tinted8::to_template_context(scheme)?;
                let rendered = tinted8::render(&self.content, &ctx)?;
                Ok(rendered)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{Scheme, Template};

    const SCHEME: &str = r##"
system: "base16"
name: "Test"
slug: "test"
author: "Me"
variant: "dark"
palette:
  base00: "#000000"
  base01: "#111111"
  base02: "#222222"
  base03: "#333333"
  base04: "#444444"
  base05: "#555555"
  base06: "#666666"
  base07: "#777777"
  base08: "#888888"
  base09: "#999999"
  base0A: "#aaaaaa"
  base0B: "#bbbbbb"
  base0C: "#cccccc"
  base0D: "#dddddd"
  base0E: "#eeeeee"
  base0F: "#ffffff"
"##;

    #[test]
    fn renders_plain_text() {
        #[allow(clippy::unwrap_used)]
        let tpl = Template::new(
            "Hello".to_string(),
            Scheme::Base16(serde_yaml::from_str(SCHEME).unwrap()),
        );
        assert_eq!(tpl.render().expect("unable to render"), "Hello");
    }

    #[test]
    fn renders_base16_variable_hex() {
        #[allow(clippy::unwrap_used)]
        let tpl = Template::new(
            "#{{base0A-hex}}".to_string(),
            Scheme::Base16(serde_yaml::from_str(SCHEME).unwrap()),
        );
        assert_eq!(tpl.render().expect("unable to render"), "#aaaaaa");
    }
}
