use std::collections::BTreeMap;

use crate::{Escaper, Templates, Type};

pub mod value;
pub use value::Value;

mod error;
pub use error::Error;
pub(crate) use error::WRONG_TYPE_MESSAGE;

mod context;
pub(crate) use context::write_tmpl;

/// checks whether a given parameter matches the given expected type
fn param_matches_ty(param: &Value, ty: &Type) -> bool {
    match (param, ty) {
        (Value::Bool(_), Type::Bool)
        | (Value::Float(_), Type::Float)
        | (Value::Int(_), Type::Int) => true,
        (v, Type::Text(_)) => v.is_printable(),
        (Value::ArrayOrTuple(values), Type::Array(inner)) => {
            let array_type = inner
                .as_ref()
                .expect("array type should be known here")
                .as_ref();
            values
                .iter()
                .all(|value| param_matches_ty(value, array_type))
        }
        (Value::ArrayOrTuple(values), Type::Tuple(tuple_types)) => {
            values.len() == tuple_types.len()
                && values
                    .iter()
                    .zip(tuple_types.iter())
                    .all(|(value, ty)| param_matches_ty(value, ty))
        }
        (Value::Struct(values), Type::Struct(struct_types)) => {
            values.len() == struct_types.len()
                && values
                    .iter()
                    .zip(struct_types.iter())
                    .all(|((key1, value), (key2, ty))| key1 == key2 && param_matches_ty(value, ty))
        }
        _ => false,
    }
}

pub(crate) fn tmpl_output<Esc: Escaper>(
    tmpls: &Templates<Esc>,
    index: usize,
    args: BTreeMap<String, Value>,
) -> Result<String, Error> {
    let mut result = String::new();

    write_tmpl(&mut result, tmpls, index, args)?;

    Ok(result)
}
