use std::collections::{BTreeMap, BTreeSet};

use configinator::{
    AuthConfig, ContentDefinition, ErrorConfig, ModelConfig, TemplateConfig, TemplateRef,
    TemplateRefField, TemplateRefFieldBind,
};
use heck::{ToPascalCase, ToSnakeCase};
use notatype::{Field, Kind};

const BASE_SERVER: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/static/server_template.rs"
));

/// returns (struct_def_internals, instantiation/runtime parse, additional_structs)
fn build_ref_fields(
    base_name: &str,
    ref_fields: &Vec<TemplateRefField>,
    fields: &Vec<Field>,
    idx_chain: Vec<u8>,
    base_access: &str,
    model_map: &BTreeMap<String, ModelConfig>,
) -> (String, String, Vec<String>) {
    let mut field_map = BTreeMap::new();

    for field in fields {
        field_map.insert(field.name.clone(), field.clone());
    }

    let mut def_fields = String::new();
    let mut instance_fields = String::new();

    let mut additional_structs = vec![];

    for ref_field in ref_fields {
        let mut idx_chain = idx_chain.clone();
        idx_chain.push(ref_field.idx);

        if let Some(field) = field_map.get(&ref_field.name) {
            if let Some(sub_fields) = &ref_field.fields {
                match &field.kind {
                    Kind::List { kind } => {
                        if let Kind::Object { name, fields } = &**kind {
                            let struct_name = format!("{}{}", base_name, name).to_pascal_case();

                            let (sub_def_fields, sub_instance_fields, sub_additional_structs) =
                                build_ref_fields(
                                    &struct_name,
                                    sub_fields,
                                    fields,
                                    vec![],
                                    &format!("{}.as_vector()", &name),
                                    model_map,
                                );

                            for additional_struct in sub_additional_structs {
                                additional_structs.push(additional_struct);
                            }

                            let struct_def = format!(
                                r#"#[derive(Serialize)]
    struct {}<'a> {{{}
    }}"#,
                                struct_name, sub_def_fields
                            );

                            additional_structs.push(struct_def);
                            def_fields = format!(
                                "{}\n  {}: Vec<{}<'a>>,",
                                def_fields, field.name, struct_name
                            );

                            instance_fields = format!(
                                r#"{}
        {}: {}.idx({}).as_vector().iter().map(|{}| {} {{{}
    }}).collect(),"#,
                                instance_fields,
                                field.name,
                                base_access,
                                field.idx,
                                name,
                                struct_name,
                                sub_instance_fields,
                            );
                        } else {
                            // todo: error
                        }
                    }
                    Kind::Object { name, fields } => {
                        let struct_name = format!("{}{}", base_name, name).to_pascal_case();

                        let (sub_def_fields, sub_instance_fields, sub_additional_structs) =
                            build_ref_fields(
                                &struct_name,
                                sub_fields,
                                fields,
                                idx_chain,
                                base_access,
                                model_map,
                            );

                        for additional_struct in sub_additional_structs {
                            additional_structs.push(additional_struct);
                        }

                        let struct_def = format!(
                            r#"#[derive(Serialize)]
struct {}<'a> {{{}
}}"#,
                            struct_name, sub_def_fields
                        );

                        additional_structs.push(struct_def);
                        def_fields =
                            format!("{}\n  {}: {}<'a>,", def_fields, field.name, struct_name);

                        instance_fields = format!(
                            r#"{}
    {}: {} {{{}
}},"#,
                            instance_fields, field.name, struct_name, sub_instance_fields,
                        );
                    }
                    Kind::Ref {
                        model,
                        field: _,
                        many,
                    } => {
                        let struct_name = format!("{}_{}", base_name, model).to_pascal_case();

                        if let Some(ref_model) = model_map.get(model) {
                            let (sub_def_fields, sub_instance_fields, sub_additional_structs) =
                                build_ref_fields(
                                    &struct_name,
                                    sub_fields,
                                    &ref_model.fields,
                                    idx_chain,
                                    base_access,
                                    model_map,
                                );

                            for additional_struct in sub_additional_structs {
                                additional_structs.push(additional_struct);
                            }

                            let struct_def = format!(
                                r#"#[derive(Serialize)]
    struct {}<'a> {{{}
    }}"#,
                                struct_name, sub_def_fields
                            );

                            additional_structs.push(struct_def);

                            if many == &Some(true) {
                                def_fields = format!(
                                    "{}\n  {}: Vec<{}<'a>>,",
                                    def_fields, field.name, struct_name
                                );
                            } else {
                                def_fields = format!(
                                    "{}\n  {}: {}<'a>,",
                                    def_fields, field.name, struct_name
                                );
                            }

                            instance_fields = format!(
                                r#"{}
        {}: {} {{{}
    }},"#,
                                instance_fields, field.name, struct_name, sub_instance_fields,
                            );
                        }
                    }
                    _ => {
                        // todo: error
                    }
                }
            } else {
                match &field.kind {
                    Kind::Object { name, fields: _ } => {
                        tracing::error!("template must list fields for object '{name}'.")
                    }
                    _ => {
                        let (t, _) = field
                            .kind
                            .to_stringified_type_w_lifetime(base_name, "a", false);
                        def_fields = format!("{}\n  {}: {},", def_fields, field.name, t);

                        let mut access = base_access.to_string();

                        for (i, idx) in idx_chain.iter().enumerate() {
                            if i == idx_chain.len() - 1 {
                                if let Some(accessor) = field.kind.to_flexbuffer_accessor() {
                                    access = format!("{}.idx({}){}", access, idx, accessor);
                                } else if let Kind::List { kind } = &field.kind {
                                    if let Some(accessor) = kind.to_flexbuffer_accessor() {
                                        access = format!(
                                            "{}.idx({}).as_vector().iter().map(|v| v{}).collect()",
                                            access, idx, accessor
                                        );
                                    } else {
                                        // ?? lists of lists not allowed for now, and list of objects
                                        // ?? handled above.
                                        // todo: error
                                    }
                                } else {
                                    // ?? shouldn't be reachable
                                    // todo: error
                                }
                            } else {
                                access = format!("{}.idx({}).as_vector()", access, idx);
                            }
                        }

                        instance_fields =
                            format!("{}\n   {}: {},", instance_fields, field.name, access);
                    }
                }
            }
        } else {
            // ?? field does not exist on model or content definition
            // todo: error
        }
    }

    (def_fields, instance_fields, additional_structs)
}

/// returns (struct_defs, instantiation/runtime parse)
fn build_content_struct_for_ref(
    template_name: &str,
    template_ref: &TemplateRef,
    content_def: &ContentDefinition,
    idx_chain: Vec<u8>,
    base_access: &str,
) -> (String, String, String) {
    let mut str_struct_defs = String::new();

    let struct_name = format!(
        "{}TemplateContent{}",
        template_name.to_pascal_case(),
        content_def.name.to_pascal_case()
    );

    let (struct_def_fields, struct_instance_fields, additional_struct_defs) =
        match &template_ref.all {
            Some(_) => build_ref_fields(
                &struct_name,
                &template_ref.fields,
                &content_def.fields,
                idx_chain,
                &format!("{}.as_vector()", &template_ref.name),
                &BTreeMap::new(),
            ),
            None => build_ref_fields(
                &struct_name,
                &template_ref.fields,
                &content_def.fields,
                idx_chain,
                base_access,
                &BTreeMap::new(),
            ),
        };

    for struct_def in additional_struct_defs {
        str_struct_defs = format!("{}\n{}", str_struct_defs, struct_def);
    }

    let struct_def = format!(
        r#"#[derive(Serialize)]
struct {}<'a> {{{}
}}"#,
        struct_name, struct_def_fields,
    );

    let struct_instantiation = match &template_ref.all {
        Some(name) => format!(
            r#" {}: {}.iter().map(|{}| {} {{{}
}}).collect(),"#,
            name,
            base_access,
            &template_ref.name,
            struct_name.to_pascal_case(),
            struct_instance_fields
        ),
        None => format!(
            r#" {}: {} {{{}
}},"#,
            template_ref.name,
            struct_name.to_pascal_case(),
            struct_instance_fields
        ),
    };

    str_struct_defs = format!("{}\n{}", str_struct_defs, struct_def);

    (struct_name, str_struct_defs, struct_instantiation)
}

fn build_model_struct_for_ref(
    template_name: &str,
    template_ref: &TemplateRef,
    model_config: &ModelConfig,
    model_config_map: &BTreeMap<String, ModelConfig>,
    idx_chain: Vec<u8>,
    base_access: &str,
    is_multi: bool,
) -> (String, String, String) {
    let mut str_struct_defs = String::new();

    let struct_name = format!(
        "{}TemplateModels{}",
        template_name.to_pascal_case(),
        model_config.name.to_pascal_case()
    );

    let mut model_fields = model_config.fields.clone();
    model_fields.push(Field {
        idx: 0,
        name: "uuid".into(),
        kind: Kind::Uuid,
        indexed: Some(true),
        queryable: None,
        searchable: None,
        mapping: None,
        doc: None,
        encrypted: None,
        compressed: None,
    });

    let (struct_def_fields, struct_instance_fields, additional_struct_defs) = match is_multi {
        true => build_ref_fields(
            &struct_name,
            &template_ref.fields,
            &model_fields,
            idx_chain,
            &format!("{}.as_vector()", &template_ref.name),
            model_config_map,
        ),
        false => build_ref_fields(
            &struct_name,
            &template_ref.fields,
            &model_fields,
            idx_chain,
            base_access,
            model_config_map,
        ),
    };

    for struct_def in additional_struct_defs {
        str_struct_defs = format!("{}\n{}", str_struct_defs, struct_def);
    }

    let struct_def = format!(
        r#"#[derive(Serialize)]
struct {}<'a> {{{}
}}"#,
        struct_name, struct_def_fields
    );

    let struct_instantiation = match is_multi {
        true => format!(
            r#" {}s: {}.iter().map(|{}| {} {{{}
}}).collect(),"#,
            &template_ref.name,
            base_access,
            &template_ref.name,
            struct_name.to_pascal_case(),
            struct_instance_fields,
        ),
        false => format!(
            r#" {}: {} {{{}
        }},"#,
            template_ref.name,
            struct_name.to_pascal_case(),
            struct_instance_fields,
        ),
    };

    str_struct_defs = format!("{}\n{}", str_struct_defs, struct_def);

    (struct_name, str_struct_defs, struct_instantiation)
}

#[allow(clippy::too_many_arguments)]
pub fn generate_template_renderers(
    domain: &String,
    version: &String,
    file_name: &String,
    template_config: TemplateConfig,
    content_def_map: &BTreeMap<String, ContentDefinition>,
    model_config_map: &BTreeMap<String, ModelConfig>,
    global_vars: BTreeMap<String, String>,
    error_config: &Option<ErrorConfig>,
    auth_config: &Option<AuthConfig>,
) -> (String, String) {
    let mut structs = vec![];
    let mut top_level_fields = vec![];
    let mut filled_structs = vec![];

    let mut idx_set = BTreeSet::new();

    let mut special_param_idx = 0;

    if let Some(params) = &template_config.params {
        let struct_name = format!("{}TemplateParams", template_config.name.to_pascal_case());
        let mut base_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            struct_name
        );

        let mut ref_instantiations = String::new();

        for param_ref in params {
            special_param_idx += 1;

            if idx_set.contains(&param_ref.idx) {
                tracing::warn!(
                    "duplicate index '{}'. will cause errors at runtime",
                    param_ref.idx
                );
            }
            idx_set.insert(param_ref.idx);

            // ?? params are always passed in as strings
            let base_access = format!("root.as_vector().idx({}).as_str()", param_ref.idx);

            base_struct_def = format!("{}\n{}: &'a str,", base_struct_def, param_ref.name);
            ref_instantiations = format!(
                "{}\n{}: {},",
                ref_instantiations, param_ref.name, base_access
            );
        }

        base_struct_def = format!("{}\n}}", base_struct_def);

        structs.push(base_struct_def);

        filled_structs.push(format!(
            r#"let params = {} {{
    {}
}};"#,
            struct_name, ref_instantiations
        ));

        top_level_fields.push(("params".into(), format!("{}<'a>", struct_name)));
    }

    if let Some(flags) = &template_config.flags {
        let struct_name = format!("{}TemplateFlags", template_config.name.to_pascal_case());
        let mut base_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            struct_name
        );

        let mut ref_instantiations = String::new();

        for flag_ref in flags {
            special_param_idx += 1;

            if idx_set.contains(&flag_ref.idx) {
                tracing::warn!(
                    "duplicate index '{}'. will cause errors at runtime",
                    flag_ref.idx
                );
            }
            idx_set.insert(flag_ref.idx);

            // ?? flags are always passed in as strings
            let base_access = format!("root.as_vector().idx({}).as_str()", flag_ref.idx);

            // todo: need flag map to check that feature exists
            base_struct_def = format!("{}\n{}: &'a str,", base_struct_def, flag_ref.name);
            ref_instantiations = format!(
                "{}\n{}: {},",
                ref_instantiations, flag_ref.name, base_access
            );
        }

        base_struct_def = format!("{}\n}}", base_struct_def);

        structs.push(base_struct_def);

        filled_structs.push(format!(
            r#"let flags = {} {{
    {}
}};"#,
            struct_name, ref_instantiations
        ));

        top_level_fields.push(("flags".into(), format!("{}<'a>", struct_name)));
    }

    if let Some(content_refs) = &template_config.content {
        let struct_name = format!("{}TemplateContent", template_config.name.to_pascal_case());
        let mut base_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            struct_name
        );

        let mut content_ref_instantiations = String::new();

        for content_ref in content_refs {
            special_param_idx += 1;

            if idx_set.contains(&content_ref.idx) {
                tracing::warn!(
                    "duplicate index '{}'. will cause errors at runtime",
                    content_ref.idx
                );
            }
            idx_set.insert(content_ref.idx);

            let idx_chain = vec![];
            let base_access = format!("root.as_vector().idx({}).as_vector()", content_ref.idx);

            if let Some(content_def) = content_def_map.get(&content_ref.name) {
                let (struct_name, str_struct_defs, struct_instantiation) =
                    build_content_struct_for_ref(
                        &template_config.name,
                        content_ref,
                        content_def,
                        idx_chain,
                        &base_access,
                    );

                content_ref_instantiations =
                    format!("{}\n{}", content_ref_instantiations, struct_instantiation);

                if let Some(name) = &content_ref.all {
                    base_struct_def =
                        format!("{}\n {}: Vec<{}<'a>>,", base_struct_def, name, struct_name);
                } else {
                    base_struct_def = format!(
                        "{}\n {}: {}<'a>,",
                        base_struct_def, content_ref.name, struct_name
                    );
                }
                structs.push(str_struct_defs);
            }
        }

        base_struct_def = format!("{}\n}}", base_struct_def);

        structs.push(base_struct_def);

        filled_structs.push(format!(
            r#"let content = {} {{
    {}
}};"#,
            struct_name, content_ref_instantiations
        ));

        top_level_fields.push(("content".into(), format!("{}<'a>", struct_name)));
    }

    if let Some(model_refs) = &template_config.models {
        let struct_name = format!("{}TemplateModels", template_config.name.to_pascal_case());
        let mut base_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            struct_name
        );

        let mut model_ref_instantiations = String::new();

        for model_ref in model_refs {
            special_param_idx += 1;

            if idx_set.contains(&model_ref.idx) {
                tracing::warn!(
                    "duplicate index '{}'. will cause errors at runtime",
                    model_ref.idx
                );
            }
            idx_set.insert(model_ref.idx);

            let mut is_vec_of = false;

            for field in &model_ref.fields {
                if let Some(bind) = &field.bind {
                    let exp = match bind {
                        TemplateRefFieldBind::Segment {
                            name: _,
                            expression,
                        } => expression,
                        TemplateRefFieldBind::Token {
                            field: _,
                            expression,
                        } => expression,
                    };

                    if exp.is_some() {
                        is_vec_of = true;
                    }
                }
            }

            let idx_chain = vec![];
            let base_access = format!("root.as_vector().idx({}).as_vector()", model_ref.idx);

            if let Some(model_def) = model_config_map.get(&model_ref.name) {
                let (struct_name, str_struct_defs, struct_instantiation) =
                    build_model_struct_for_ref(
                        &template_config.name,
                        model_ref,
                        model_def,
                        model_config_map,
                        idx_chain,
                        &base_access,
                        is_vec_of,
                    );

                model_ref_instantiations =
                    format!("{}\n{}", model_ref_instantiations, struct_instantiation);

                if is_vec_of {
                    base_struct_def = format!(
                        "{}\n {}s: Vec<{}<'a>>,",
                        base_struct_def, model_ref.name, struct_name
                    );
                } else {
                    base_struct_def = format!(
                        "{}\n {}: {}<'a>,",
                        base_struct_def, model_ref.name, struct_name
                    );
                }

                structs.push(str_struct_defs);
            }
        }

        base_struct_def = format!("{}\n}}", base_struct_def);

        structs.push(base_struct_def);

        filled_structs.push(format!(
            r#"let models = {} {{
    {}
}};"#,
            struct_name, model_ref_instantiations
        ));

        top_level_fields.push(("models".into(), format!("{}<'a>", struct_name)));
    }

    if let Some(globals) = template_config.globals {
        let prefix = format!("{}TemplateGlobals", template_config.name.to_pascal_case());

        top_level_fields.push(("globals".to_string(), format!("{}<'a>", prefix)));

        let mut str_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            &prefix
        );
        let mut str_struct_fill = format!("let globals = {} {{", &prefix);

        for global in globals {
            str_struct_def = format!("{}\n   {}: &'a str,", str_struct_def, global);

            if let Some(value) = global_vars.get(&global) {
                str_struct_fill =
                    format!("{}\n      {}: r#\"{}\"#,", str_struct_fill, global, value);
            }
        }

        str_struct_def = format!("{}\n}}", str_struct_def);
        str_struct_fill = format!("{}\n    }};", str_struct_fill);

        structs.push(str_struct_def);
        filled_structs.push(str_struct_fill);
    };

    if let Some(fields) = template_config.fields {
        let prefix = format!("{}TemplateFields", template_config.name.to_pascal_case());

        top_level_fields.push(("template".to_string(), format!("{}<'a>", prefix)));

        let mut str_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            &prefix
        );
        let mut str_struct_fill = format!("let template = {} {{", &prefix);

        for field in fields {
            str_struct_def = format!("{}\n   {}: &'a str,", str_struct_def, field.name);
            str_struct_fill = format!(
                "{}\n      {}: r#\"{}\"#,",
                str_struct_fill, field.name, field.value
            );
        }

        str_struct_def = format!("{}\n}}", str_struct_def);
        str_struct_fill = format!("{}\n    }};", str_struct_fill);

        structs.push(str_struct_def);
        filled_structs.push(str_struct_fill);
    }

    let mut is_error_template = false;

    if let Some(error_config) = error_config
        && error_config.template == template_config.name {
        let struct_name = format!("{}TemplateError", template_config.name.to_pascal_case());
        let mut base_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            struct_name
        );

        if idx_set.contains(&special_param_idx) {
            tracing::warn!(
                    "duplicate index '{}'. will cause errors at runtime",
                    special_param_idx
                );
        }
        idx_set.insert(special_param_idx);

        is_error_template = true;

        let mut ref_instantiations = String::new();

        let base_access = format!("root.as_vector().idx({}).as_vector()", special_param_idx);

        base_struct_def = format!("{}\nmessage: &'a str,", base_struct_def);
        base_struct_def = format!("{}\ncode: u16,", base_struct_def);

        ref_instantiations = format!(
            "{}\nmessage: {}.idx(0).as_str(),",
            ref_instantiations, base_access
        );
        ref_instantiations = format!(
            "{}\ncode: {}.idx(1).as_u16(),",
            ref_instantiations, base_access
        );

        base_struct_def = format!("{}\n}}", base_struct_def);

        structs.push(base_struct_def);

        filled_structs.push(format!(
            r#"let error = {} {{
    {}
}};"#,
            struct_name, ref_instantiations
        ));

        top_level_fields.push(("error".into(), format!("{}<'a>", struct_name)));

        // ?? error always comes first if it exists
        special_param_idx += 1;
    }

    let mut is_totp_template = false;

    if let Some(auth_config) = auth_config
        && let Some(totp_template) = &auth_config.mfa.totp.template
        && totp_template == &template_config.name {
        let struct_name = format!("{}TemplateTotp", template_config.name.to_pascal_case());
        let mut base_struct_def = format!(
            r#"#[derive(Serialize)]
struct {}<'a> {{"#,
            struct_name
        );

        if idx_set.contains(&special_param_idx) {
            tracing::warn!(
                        "duplicate index '{}'. will cause errors at runtime",
                        special_param_idx
                    );
        }
        idx_set.insert(special_param_idx);

        is_totp_template = true;

        let mut ref_instantiations = String::new();

        let base_access =
            format!("root.as_vector().idx({}).as_vector()", special_param_idx);

        base_struct_def = format!("{}\nqr_code: &'a str,", base_struct_def);
        base_struct_def = format!("{}\naccount: &'a str,", base_struct_def);
        base_struct_def = format!("{}\nrecovery_codes: Vec<&'a str>,", base_struct_def);

        ref_instantiations = format!(
            "{}\nqr_code: {}.idx(0).as_str(),",
            ref_instantiations, base_access
        );
        ref_instantiations = format!(
            "{}\naccount: {}.idx(1).as_str(),",
            ref_instantiations, base_access
        );
        ref_instantiations = format!(
            "{}\nrecovery_codes: {}.idx(2).as_vector().iter().map(|v| v.as_str()).collect(),",
            ref_instantiations, base_access
        );

        base_struct_def = format!("{}\n}}", base_struct_def);

        structs.push(base_struct_def);

        filled_structs.push(format!(
            r#"let totp = {} {{
    {}
}};"#,
            struct_name, ref_instantiations
        ));

        top_level_fields.push(("totp".into(), format!("{}<'a>", struct_name)));
    }

    let mut str_structs = String::new();
    let mut str_filled_structs = String::new();

    for str_struct in structs {
        str_structs = format!("{}{}\n", str_structs, str_struct);
    }

    for str_filled_struct in filled_structs {
        str_filled_structs = format!("  {}{}\n", str_filled_structs, str_filled_struct);
    }

    // todo: domain and version should be in here
    let mut top_level_interior = "\n    domain: &'a str,\n  version: &'a str,".to_string();
    let mut guts = format!("    domain: \"{}\",\n   version: \"{}\",", domain, version);

    for (field_name, struct_name) in top_level_fields {
        top_level_interior = format!("{}  {}: {},\n", top_level_interior, field_name, struct_name);
        guts = format!("{}        {},\n", guts, field_name);
    }

    let server = format!(
        r#"{}
{}

#[derive(Template)]
#[template(path = "{}")]
struct {}Template<'a> {{{}}}

fn main() -> Result<(), Box<dyn std::error::Error>> {{
    let args: Option<Vec<u8>> = recv_in()?;

    {}

    {}

    let rendered = {}Template {{
{}
    }}.render()?;

    send_out(Some(rendered.as_bytes().to_vec()))
}}
"#,
        BASE_SERVER,
        str_structs,
        file_name,
        &template_config.name.to_pascal_case(),
        &top_level_interior,
        // ?? use crate features instead?
        if template_config.models.is_some()
            || template_config.content.is_some()
            || is_error_template
            || is_totp_template
        {
            "let root = match args {
        Some(args) => args,
        None => {
            let root = flexbuffers::Builder::new(flexbuffers::BuilderOptions::SHARE_NONE);
            root.view().to_vec()
        }
    };

    let root = flexbuffers::Reader::get_root(&root[..])?;"
        } else {
            ""
        },
        &str_filled_structs,
        &template_config.name.to_pascal_case(),
        &guts,
    );

    let client = format!(
        r#"{}

#[derive(Template)]
#[template(path = "{}")]
struct {}Template<'a> {{{}}}

#[wasm_bindgen]
pub fn render_{}(args: Option<Vec<u8>>) -> Result<String, String> {{
    let root = match args {{
        Some(args) => args,
        None => {{
            let root = flexbuffers::Builder::new(flexbuffers::BuilderOptions::SHARE_NONE);
            root.view().to_vec()
        }}
    }};

    match flexbuffers::Reader::get_root(&root[..]) {{
        Ok(root) => {{
            {}

            match ({}Template {{
                {}
            }}).render() {{
                Ok(res) => Ok(res),
                Err(err) => Err(err.to_string())
            }}
        }}
        Err(err) => Err(err.to_string())
    }}
}}
"#,
        str_structs,
        file_name,
        &template_config.name.to_pascal_case(),
        &top_level_interior,
        &template_config.name.to_snake_case(),
        &str_filled_structs,
        &template_config.name.to_pascal_case(),
        &guts,
    );

    (server, client)
}
