use std::fmt::{self, Debug, Display};

use crate::Type;

use super::Value;

#[derive(Debug)]
/// An error which can occur when attempting to render a template.
pub enum Error {
    /// Received a [`Value`] that is incompatible with the templates expected parameter type
    WrongParameters {
        /// This is the [`Type`] that was expected; the type of the parameters of the template you
        /// attempted to render.
        expected: Type,
        /// This is the value we received which did not match the expected [`Type`]
        got: Value,
    },
    /// Received a non struct root [`Value`]. A templates arguments need to always be contained in a [`Value::Struct`]
    /// at the top level but the value this render call received was not one.
    NonStructRootValue,
    /// An error occured writing to the [`fmt::Write`] implementation you provided.
    Fmt(fmt::Error),
    /// An escaper returned the following error.
    Escape(String),
    /// A function returned the following error.
    Function(Box<maybe_sync::dyn_maybe_send_sync!(std::error::Error)>),
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::WrongParameters { expected, got } => {
                write!(f, "Wrong parameters; expected {expected} but got {got:?}")
            }

            Self::NonStructRootValue => write!(
                f,
                "Root value needs to be a struct but received non struct value instead"
            ),

            Self::Escape(err) => write!(f, "Error escaping: {err}"),

            Self::Fmt(err) => write!(f, "Failed to write: {err}"),

            Self::Function(err) => write!(f, "Function returned error: {err}"),
        }
    }
}

impl std::error::Error for Error {}

pub(crate) const WRONG_TYPE_MESSAGE: &str =
    "this should have been caught by type checking or a function misbehaved";
