use minijinja::Environment;

static TEMPLATES: &[(&str, &str)] = &[
    (
        "last_error_helper.jinja",
        include_str!("templates/last_error_helper.jinja"),
    ),
    (
        "unmarshal_bytes_helper.jinja",
        include_str!("templates/unmarshal_bytes_helper.jinja"),
    ),
    ("ptr_helper.jinja", include_str!("templates/ptr_helper.jinja")),
    ("opaque_type.jinja", include_str!("templates/opaque_type.jinja")),
    ("go_file_header.jinja", include_str!("templates/go_file_header.jinja")),
    (
        "plugin_bridge_exports.jinja",
        include_str!("templates/plugin_bridge_exports.jinja"),
    ),
    ("unit_enum.jinja", include_str!("templates/unit_enum.jinja")),
    (
        "visitor_preamble.jinja",
        include_str!("templates/visitor_preamble.jinja"),
    ),
    (
        "visitor_node_context_and_result.jinja",
        include_str!("templates/visitor_node_context_and_result.jinja"),
    ),
    (
        "package_declaration.jinja",
        include_str!("templates/package_declaration.jinja"),
    ),
    (
        "package_doc_and_declaration.jinja",
        include_str!("templates/package_doc_and_declaration.jinja"),
    ),
    (
        "cgo_preamble_binding.jinja",
        include_str!("templates/cgo_preamble_binding.jinja"),
    ),
    (
        "cgo_preamble_visitor.jinja",
        include_str!("templates/cgo_preamble_visitor.jinja"),
    ),
    ("imports_basic.jinja", include_str!("templates/imports_basic.jinja")),
    (
        "string_type_decl.jinja",
        include_str!("templates/string_type_decl.jinja"),
    ),
    (
        "const_block_header.jinja",
        include_str!("templates/const_block_header.jinja"),
    ),
    (
        "const_block_footer.jinja",
        include_str!("templates/const_block_footer.jinja"),
    ),
    ("const_variant.jinja", include_str!("templates/const_variant.jinja")),
    (
        "struct_type_decl.jinja",
        include_str!("templates/struct_type_decl.jinja"),
    ),
    ("struct_type_end.jinja", include_str!("templates/struct_type_end.jinja")),
    ("struct_field.jinja", include_str!("templates/struct_field.jinja")),
    ("blank_line.jinja", include_str!("templates/blank_line.jinja")),
    (
        "function_signature.jinja",
        include_str!("templates/function_signature.jinja"),
    ),
    (
        "function_body_end.jinja",
        include_str!("templates/function_body_end.jinja"),
    ),
    ("export_marker.jinja", include_str!("templates/export_marker.jinja")),
    (
        "node_context_struct.jinja",
        include_str!("templates/node_context_struct.jinja"),
    ),
    (
        "visit_result_struct.jinja",
        include_str!("templates/visit_result_struct.jinja"),
    ),
    (
        "visit_result_helper.jinja",
        include_str!("templates/visit_result_helper.jinja"),
    ),
    (
        "visitor_interface_header.jinja",
        include_str!("templates/visitor_interface_header.jinja"),
    ),
    (
        "visitor_interface_footer.jinja",
        include_str!("templates/visitor_interface_footer.jinja"),
    ),
    (
        "base_visitor_header.jinja",
        include_str!("templates/base_visitor_header.jinja"),
    ),
    (
        "visitor_registry_block.jinja",
        include_str!("templates/visitor_registry_block.jinja"),
    ),
    (
        "decode_node_context.jinja",
        include_str!("templates/decode_node_context.jinja"),
    ),
    (
        "encode_visit_result.jinja",
        include_str!("templates/encode_visit_result.jinja"),
    ),
    ("opt_go_string.jinja", include_str!("templates/opt_go_string.jinja")),
    (
        "decode_cells_json.jinja",
        include_str!("templates/decode_cells_json.jinja"),
    ),
    (
        "trampoline_lookup.jinja",
        include_str!("templates/trampoline_lookup.jinja"),
    ),
    (
        "trampoline_is_header.jinja",
        include_str!("templates/trampoline_is_header.jinja"),
    ),
    (
        "convert_with_visitor_helper.jinja",
        include_str!("templates/convert_with_visitor_helper.jinja"),
    ),
    (
        "convert_with_visitor_wrapper.jinja",
        include_str!("templates/convert_with_visitor_wrapper.jinja"),
    ),
    (
        "enum_type_header.jinja",
        include_str!("templates/enum_type_header.jinja"),
    ),
    (
        "enum_type_footer.jinja",
        include_str!("templates/enum_type_footer.jinja"),
    ),
    (
        "go_doc_comment_line.jinja",
        include_str!("templates/go_doc_comment_line.jinja"),
    ),
    (
        "go_const_declaration.jinja",
        include_str!("templates/go_const_declaration.jinja"),
    ),
    (
        "extern_trampoline_decl.jinja",
        include_str!("templates/extern_trampoline_decl.jinja"),
    ),
    (
        "plugin_trampoline_decl.jinja",
        include_str!("templates/plugin_trampoline_decl.jinja"),
    ),
    (
        "vtable_method_field.jinja",
        include_str!("templates/vtable_method_field.jinja"),
    ),
    (
        "plugin_trampoline_lifecycle.jinja",
        include_str!("templates/plugin_trampoline_lifecycle.jinja"),
    ),
    (
        "trait_interface_method.jinja",
        include_str!("templates/trait_interface_method.jinja"),
    ),
    (
        "register_vtable_method_field.jinja",
        include_str!("templates/register_vtable_method_field.jinja"),
    ),
    (
        "base_visitor_method.jinja",
        include_str!("templates/base_visitor_method.jinja"),
    ),
    (
        "trait_interface_header.jinja",
        include_str!("templates/trait_interface_header.jinja"),
    ),
    (
        "plugin_method_signature.jinja",
        include_str!("templates/plugin_method_signature.jinja"),
    ),
    (
        "register_function_header.jinja",
        include_str!("templates/register_function_header.jinja"),
    ),
    (
        "unregister_function_header.jinja",
        include_str!("templates/unregister_function_header.jinja"),
    ),
    (
        "vtable_struct_init.jinja",
        include_str!("templates/vtable_struct_init.jinja"),
    ),
    ("register_c_call.jinja", include_str!("templates/register_c_call.jinja")),
    (
        "unregister_c_call.jinja",
        include_str!("templates/unregister_c_call.jinja"),
    ),
    (
        "unregister_fn_header.jinja",
        include_str!("templates/unregister_fn_header.jinja"),
    ),
    (
        "clear_function_header.jinja",
        include_str!("templates/clear_function_header.jinja"),
    ),
    ("clear_c_call.jinja", include_str!("templates/clear_c_call.jinja")),
    (
        "visitor_interface_method.jinja",
        include_str!("templates/visitor_interface_method.jinja"),
    ),
    (
        "trampoline_param_decode.jinja",
        include_str!("templates/trampoline_param_decode.jinja"),
    ),
    (
        "trampoline_func_call.jinja",
        include_str!("templates/trampoline_func_call.jinja"),
    ),
    ("c_var_decl.jinja", include_str!("templates/c_var_decl.jinja")),
    (
        "c_func_call_assign.jinja",
        include_str!("templates/c_func_call_assign.jinja"),
    ),
    ("c_defer_free.jinja", include_str!("templates/c_defer_free.jinja")),
    ("c_func_call.jinja", include_str!("templates/c_func_call.jinja")),
    ("package_and_cgo.jinja", include_str!("templates/package_and_cgo.jinja")),
    (
        "trampoline_signature.jinja",
        include_str!("templates/trampoline_signature.jinja"),
    ),
    (
        "handle_type_assertion.jinja",
        include_str!("templates/handle_type_assertion.jinja"),
    ),
    (
        "plugin_method_trampoline_header.jinja",
        include_str!("templates/plugin_method_trampoline_header.jinja"),
    ),
    ("type_doc_header.jinja", include_str!("templates/type_doc_header.jinja")),
    (
        "enum_string_method.jinja",
        include_str!("templates/enum_string_method.jinja"),
    ),
    (
        "enum_marshal_json.jinja",
        include_str!("templates/enum_marshal_json.jinja"),
    ),
    (
        "passthrough_raw_message_enum_body.jinja",
        include_str!("templates/passthrough_raw_message_enum_body.jinja"),
    ),
    (
        "untagged_union_marshalers.jinja",
        include_str!("templates/untagged_union_marshalers.jinja"),
    ),
    (
        "enum_unmarshal_json.jinja",
        include_str!("templates/enum_unmarshal_json.jinja"),
    ),
    ("variant_comment.jinja", include_str!("templates/variant_comment.jinja")),
    ("tag_field.jinja", include_str!("templates/tag_field.jinja")),
    ("variant_field.jinja", include_str!("templates/variant_field.jinja")),
    (
        "method_receiver_static.jinja",
        include_str!("templates/method_receiver_static.jinja"),
    ),
    (
        "method_receiver_instance.jinja",
        include_str!("templates/method_receiver_instance.jinja"),
    ),
    ("method_return.jinja", include_str!("templates/method_return.jinja")),
    (
        "method_empty_return.jinja",
        include_str!("templates/method_empty_return.jinja"),
    ),
    (
        "method_signature_instance.jinja",
        include_str!("templates/method_signature_instance.jinja"),
    ),
    (
        "method_signature_static.jinja",
        include_str!("templates/method_signature_static.jinja"),
    ),
    (
        "streaming_method_signature.jinja",
        include_str!("templates/streaming_method_signature.jinja"),
    ),
    (
        "streaming_method_body.jinja",
        include_str!("templates/streaming_method_body.jinja"),
    ),
    ("free_string.jinja", include_str!("templates/free_string.jinja")),
    ("free_type.jinja", include_str!("templates/free_type.jinja")),
    (
        "visitor_field_doc.jinja",
        include_str!("templates/visitor_field_doc.jinja"),
    ),
    ("visitor_field.jinja", include_str!("templates/visitor_field.jinja")),
    ("c_options_var.jinja", include_str!("templates/c_options_var.jinja")),
    (
        "c_call_with_json.jinja",
        include_str!("templates/c_call_with_json.jinja"),
    ),
    (
        "bytes_to_c_pointer.jinja",
        include_str!("templates/bytes_to_c_pointer.jinja"),
    ),
    (
        "tagged_union_struct_header.jinja",
        include_str!("templates/tagged_union_struct_header.jinja"),
    ),
    (
        "tagged_union_tag_field.jinja",
        include_str!("templates/tagged_union_tag_field.jinja"),
    ),
    (
        "tagged_union_variant_field.jinja",
        include_str!("templates/tagged_union_variant_field.jinja"),
    ),
    (
        "tagged_union_marshal_json_header.jinja",
        include_str!("templates/tagged_union_marshal_json_header.jinja"),
    ),
    (
        "tagged_union_marshal_variant.jinja",
        include_str!("templates/tagged_union_marshal_variant.jinja"),
    ),
    (
        "tagged_union_marshal_json_footer.jinja",
        include_str!("templates/tagged_union_marshal_json_footer.jinja"),
    ),
    (
        "tagged_union_unmarshal_json_header.jinja",
        include_str!("templates/tagged_union_unmarshal_json_header.jinja"),
    ),
    (
        "tagged_union_unmarshal_variant.jinja",
        include_str!("templates/tagged_union_unmarshal_variant.jinja"),
    ),
    (
        "tagged_union_unmarshal_json_footer.jinja",
        include_str!("templates/tagged_union_unmarshal_json_footer.jinja"),
    ),
    (
        "struct_marshal_json_header.jinja",
        include_str!("templates/struct_marshal_json_header.jinja"),
    ),
    (
        "struct_marshal_aux_field.jinja",
        include_str!("templates/struct_marshal_aux_field.jinja"),
    ),
    (
        "struct_marshal_aux_init.jinja",
        include_str!("templates/struct_marshal_aux_init.jinja"),
    ),
    (
        "struct_marshal_bytes_field_pointer.jinja",
        include_str!("templates/struct_marshal_bytes_field_pointer.jinja"),
    ),
    (
        "struct_marshal_bytes_field_nonpointer.jinja",
        include_str!("templates/struct_marshal_bytes_field_nonpointer.jinja"),
    ),
    (
        "struct_marshal_regular_field.jinja",
        include_str!("templates/struct_marshal_regular_field.jinja"),
    ),
    (
        "struct_marshal_json_footer.jinja",
        include_str!("templates/struct_marshal_json_footer.jinja"),
    ),
    (
        "config_option_type_header.jinja",
        include_str!("templates/config_option_type_header.jinja"),
    ),
    (
        "config_with_option_comment.jinja",
        include_str!("templates/config_with_option_comment.jinja"),
    ),
    (
        "config_with_option_signature.jinja",
        include_str!("templates/config_with_option_signature.jinja"),
    ),
    (
        "config_new_constructor_header.jinja",
        include_str!("templates/config_new_constructor_header.jinja"),
    ),
    (
        "config_default_field.jinja",
        include_str!("templates/config_default_field.jinja"),
    ),
    (
        "config_new_constructor_footer.jinja",
        include_str!("templates/config_new_constructor_footer.jinja"),
    ),
    (
        "marshal_receiver_to_c.jinja",
        include_str!("templates/marshal_receiver_to_c.jinja"),
    ),
    (
        "c_call_with_ptr_assign.jinja",
        include_str!("templates/c_call_with_ptr_assign.jinja"),
    ),
    ("c_call_unit.jinja", include_str!("templates/c_call_unit.jinja")),
    (
        "free_string_on_error.jinja",
        include_str!("templates/free_string_on_error.jinja"),
    ),
    (
        "return_value_and_nil.jinja",
        include_str!("templates/return_value_and_nil.jinja"),
    ),
    (
        "receiver_ptr_assign.jinja",
        include_str!("templates/receiver_ptr_assign.jinja"),
    ),
    (
        "param_string_optional.jinja",
        include_str!("templates/param_string_optional.jinja"),
    ),
    (
        "param_string_required.jinja",
        include_str!("templates/param_string_required.jinja"),
    ),
    (
        "param_opaque_cast.jinja",
        include_str!("templates/param_opaque_cast.jinja"),
    ),
    (
        "param_named_type.jinja",
        include_str!("templates/param_named_type.jinja"),
    ),
    (
        "param_vec_or_map.jinja",
        include_str!("templates/param_vec_or_map.jinja"),
    ),
    (
        "param_optional_opaque.jinja",
        include_str!("templates/param_optional_opaque.jinja"),
    ),
    (
        "param_optional_named_inline.jinja",
        include_str!("templates/param_optional_named_inline.jinja"),
    ),
    (
        "param_optional_decl.jinja",
        include_str!("templates/param_optional_decl.jinja"),
    ),
    (
        "param_primitive_bool.jinja",
        include_str!("templates/param_primitive_bool.jinja"),
    ),
    (
        "param_primitive_numeric.jinja",
        include_str!("templates/param_primitive_numeric.jinja"),
    ),
    (
        "param_optional_primitive_bool.jinja",
        include_str!("templates/param_optional_primitive_bool.jinja"),
    ),
    (
        "param_optional_primitive_numeric.jinja",
        include_str!("templates/param_optional_primitive_numeric.jinja"),
    ),
    (
        "c_call_with_ptr_assign.jinja",
        include_str!("templates/c_call_with_ptr_assign.jinja"),
    ),
    (
        "plugin_free_user_data_extern.jinja",
        include_str!("templates/plugin_free_user_data_extern.jinja"),
    ),
    (
        "vtable_free_user_data_field.jinja",
        include_str!("templates/vtable_free_user_data_field.jinja"),
    ),
    (
        "impl_method_call_err.jinja",
        include_str!("templates/impl_method_call_err.jinja"),
    ),
    (
        "impl_method_call_result_err.jinja",
        include_str!("templates/impl_method_call_result_err.jinja"),
    ),
    (
        "impl_method_call_result.jinja",
        include_str!("templates/impl_method_call_result.jinja"),
    ),
    (
        "plugin_free_user_data_func.jinja",
        include_str!("templates/plugin_free_user_data_func.jinja"),
    ),
    ("go_string_cast.jinja", include_str!("templates/go_string_cast.jinja")),
    ("var_bytes_decl.jinja", include_str!("templates/var_bytes_decl.jinja")),
    ("if_nil_check.jinja", include_str!("templates/if_nil_check.jinja")),
    ("var_assign.jinja", include_str!("templates/var_assign.jinja")),
    ("var_type_decl.jinja", include_str!("templates/var_type_decl.jinja")),
    (
        "json_unmarshal_unsafe.jinja",
        include_str!("templates/json_unmarshal_unsafe.jinja"),
    ),
    (
        "json_unmarshal_simple.jinja",
        include_str!("templates/json_unmarshal_simple.jinja"),
    ),
    ("var_assign_m.jinja", include_str!("templates/var_assign_m.jinja")),
    ("var_assign_cast.jinja", include_str!("templates/var_assign_cast.jinja")),
    (
        "json_unmarshal_rawdata.jinja",
        include_str!("templates/json_unmarshal_rawdata.jinja"),
    ),
    (
        "c_options_var_decl.jinja",
        include_str!("templates/c_options_var_decl.jinja"),
    ),
    (
        "c_options_from_json.jinja",
        include_str!("templates/c_options_from_json.jinja"),
    ),
    (
        "c_options_defer_free.jinja",
        include_str!("templates/c_options_defer_free.jinja"),
    ),
    (
        "c_options_from_value.jinja",
        include_str!("templates/c_options_from_value.jinja"),
    ),
    ("c_bridge_new.jinja", include_str!("templates/c_bridge_new.jinja")),
    (
        "c_bridge_defer_free.jinja",
        include_str!("templates/c_bridge_defer_free.jinja"),
    ),
    (
        "c_options_set_visitor.jinja",
        include_str!("templates/c_options_set_visitor.jinja"),
    ),
    ("c_convert_call.jinja", include_str!("templates/c_convert_call.jinja")),
    (
        "c_result_defer_free.jinja",
        include_str!("templates/c_result_defer_free.jinja"),
    ),
    (
        "c_result_to_json.jinja",
        include_str!("templates/c_result_to_json.jinja"),
    ),
    (
        "method_update_from_json.jinja",
        include_str!("templates/method_update_from_json.jinja"),
    ),
    (
        "method_return_simple.jinja",
        include_str!("templates/method_return_simple.jinja"),
    ),
    ("c_call_simple.jinja", include_str!("templates/c_call_simple.jinja")),
    (
        "method_receiver_ptr_assign.jinja",
        include_str!("templates/method_receiver_ptr_assign.jinja"),
    ),
    (
        "trampoline_func_signature.jinja",
        include_str!("templates/trampoline_func_signature.jinja"),
    ),
    ("c_ptr_assign.jinja", include_str!("templates/c_ptr_assign.jinja")),
    (
        "free_type_on_error.jinja",
        include_str!("templates/free_type_on_error.jinja"),
    ),
    ("c_json_to_json.jinja", include_str!("templates/c_json_to_json.jinja")),
    ("var_decl_type.jinja", include_str!("templates/var_decl_type.jinja")),
    (
        "json_unmarshal_error.jinja",
        include_str!("templates/json_unmarshal_error.jinja"),
    ),
    ("var_decl_slice.jinja", include_str!("templates/var_decl_slice.jinja")),
    (
        "c_options_from_json_with_name.jinja",
        include_str!("templates/c_options_from_json_with_name.jinja"),
    ),
    (
        "c_options_defer_free_with_name.jinja",
        include_str!("templates/c_options_defer_free_with_name.jinja"),
    ),
    (
        "c_ptr_assign_func.jinja",
        include_str!("templates/c_ptr_assign_func.jinja"),
    ),
    (
        "c_conversion_result_free.jinja",
        include_str!("templates/c_conversion_result_free.jinja"),
    ),
    (
        "c_conversion_result_to_json.jinja",
        include_str!("templates/c_conversion_result_to_json.jinja"),
    ),
    (
        "c_free_string_defer.jinja",
        include_str!("templates/c_free_string_defer.jinja"),
    ),
    (
        "bytes_result_call.jinja",
        include_str!("templates/bytes_result_call.jinja"),
    ),
];

pub(crate) fn make_env() -> Environment<'static> {
    let mut env = Environment::new();
    env.set_trim_blocks(true);
    env.set_lstrip_blocks(true);
    // Preserve the trailing newline that each template file ends with.
    // Inline-fragment templates (method_receiver_*, method_return*, method_empty_return)
    // intentionally do NOT end with \n so they can be concatenated on the same line.
    env.set_keep_trailing_newline(true);
    for (name, src) in TEMPLATES {
        env.add_template(name, src).expect("built-in template is valid");
    }
    env
}

pub(crate) fn render(template_name: &str, ctx: minijinja::Value) -> String {
    make_env()
        .get_template(template_name)
        .unwrap_or_else(|_| panic!("template {template_name} not found"))
        .render(ctx)
        .unwrap_or_else(|e| panic!("template {template_name} failed to render: {e}"))
}
