use std::path::Path;

use tera::Tera;

#[derive(Debug, Clone)]
pub struct Theme {
    generator: Tera,
}

#[derive(Debug)]
pub enum ThemeLoadErr {
    ThemeDirNotFound,
    ThemeTemplatesDirNotFound,
    TeraNewErr(tera::Error),
}

impl Theme {
    pub fn new(theme_path: &Path) -> Result<Self, ThemeLoadErr> {
        if !theme_path.is_dir() {
            eprintln!(
                "Error: theme path '{}' is not a directory",
                theme_path.to_string_lossy()
            );
            return Err(ThemeLoadErr::ThemeDirNotFound);
        }
        let templates_path = theme_path.join("templates");
        if !templates_path.is_dir() {
            eprintln!(
                "Error: templates path '{}' is not a directory",
                templates_path.to_string_lossy()
            );
            return Err(ThemeLoadErr::ThemeTemplatesDirNotFound);
        }
        let generator = Tera::new(&format!("{}/**/*", templates_path.to_string_lossy()))
            .map_err(ThemeLoadErr::TeraNewErr)?;
        Ok(Self { generator })
    }

    /// Merge multiple themes together, resulting in a combined [`Tera`]
    /// generator.
    ///
    /// If multiple themes are present, the first theme acts as a base, any
    /// subsequent theme overwrites any content provided by the previous theme.
    /// Finally template files inside the `templates` directory inside the
    /// website project directory will take precedence over any other theme
    /// (this last behaviour is defined in code in [`crate::site::Site::new`])
    pub fn merge(themes: &[Self]) -> Result<Tera, tera::Error> {
        if themes.len() == 1 {
            return Ok(themes.first().unwrap().generator.clone());
        }
        let mut out = Tera::default();
        // Why reverse the theme order?
        // It makes sense that layering a theme overrides the previous
        // definitions, but tera works the other way around. Reversing is a
        // simple workaround
        for theme in themes.iter().rev() {
            out.extend(&theme.generator)?;
        }

        Ok(out)
    }
}

impl Default for Theme {
    fn default() -> Self {
        let mut generator = Tera::default();

        generator.add_raw_template("__base__.html", r#"<!DOCTYPE html>
<html lang="{{ site.config.language }}">
<head>
    {% block head %}
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% if page and page.metadata and page.metadata.title %}{{page.metadata.title}} - {%endif%}{{ site.config.title }}</title>
    {% endblock head %}
</head>
<body>
    <header>
        <h1>
            <a href="{{ site.config.base_url | default(value="/") | safe }}">{{ site.config.title }}</a>
        </h1>
    </header>
    <main>
        {% block content %}
        {% endblock content %}
    </main>
    <hr>
    <footer>
        <p>
            {{ now() | date(format="%Y") }} &copy;
            {{ site.config.authors | join(sep=", ") }}
        </p>
    </footer>
</body>
</html>"#).unwrap();
        generator
            .add_raw_template(
                "__page__.html",
                r#"{% extends "__base__.html" %}
{% block content %}
    <article>
        {{ page.html | safe }}
    </article>
{% endblock content %}"#,
            )
            .unwrap();
        generator
            .add_raw_template(
                "index.html",
                r#"{% extends "__base__.html" %}
{% block content %}
    <h2>Pages:</h2>
    <ul>
    {% for page in site.pages %}
        <li><a href="{{page.url}}">
            {% if page.metadata.title %}
                {{page.metadata.title}}
            {% else %}
                {{page.url}}
            {% endif %}
        </a></li>
    {% endfor %}
    </ul>
{% endblock content %}"#,
            )
            .unwrap();

        Self { generator }
    }
}

#[cfg(test)]
mod tests {
    use super::Theme;

    /// this test exists because of unwraps when using the default templates
    #[test]
    fn theme_default() {
        Theme::default();
    }
}
