use log::error;
use serde::Serialize;

use crate::errors::UserError;

pub fn render_template(
    handlebars: &handlebars::Handlebars,
    name: &str,
    args: &TemplateArgs,
) -> actix_web::Result<String> {
    if handlebars.has_template(name) {
        Ok(handlebars
            .render(name, args)
            .map_err(|_| UserError::InternalError)?)
    } else {
        error!("template not found: {}", name);
        Err(UserError::NotFound)?
    }
}

/** All arguments that can be given to a template. */
#[derive(Default, Serialize)]
pub struct TemplateArgs {
    flash: Option<Flash>,
    error: Option<ErrorMessage>,
}

impl TemplateArgs {
    pub fn builder() -> TemplateArgsBuilder {
        TemplateArgsBuilder::new()
    }
}

pub struct TemplateArgsBuilder {
    flash: Option<Flash>,
    error: Option<ErrorMessage>,
}

impl TemplateArgsBuilder {
    pub fn new() -> Self {
        TemplateArgsBuilder {
            flash: None,
            error: None,
        }
    }

    pub fn flash(mut self, flash: Flash) -> Self {
        self.flash = Some(flash);
        self
    }

    pub fn error_page(mut self, error: ErrorMessage) -> Self {
        self.error = Some(error);
        self
    }

    pub fn build(self) -> TemplateArgs {
        TemplateArgs {
            flash: self.flash,
            error: self.error,
        }
    }
}

/** A flash message that should be displayed as a notification on the page. */
#[derive(Serialize)]
pub struct Flash {
    message: String,
    #[serde(rename = "type")]
    level: FlashType,
}

impl Flash {
    pub fn new(message: &str, level: FlashType) -> Self {
        Self {
            message: message.to_string(),
            level,
        }
    }
}

#[derive(Serialize)]
#[serde(rename_all = "lowercase")]
pub enum FlashType {
    Info,
    Success,
    Warning,
    Danger,
}

/** Contents of an error page. */
#[derive(Serialize)]
pub struct ErrorMessage {
    message: String,
    status_code: u16,
}

impl ErrorMessage {
    pub fn new(message: &str, status_code: u16) -> Self {
        Self {
            message: message.to_string(),
            status_code,
        }
    }
}