use crate::core::ir::EnumDef;

use super::ConversionConfig;
use super::helpers::{binding_to_core_match_arm_ext_cfg, core_enum_path_remapped, core_to_binding_match_arm_ext_cfg};

/// Generate `impl From<BindingEnum> for core::Enum` (binding -> core).
pub fn gen_enum_from_binding_to_core(enum_def: &EnumDef, core_import: &str) -> String {
    gen_enum_from_binding_to_core_cfg(enum_def, core_import, &ConversionConfig::default())
}

/// Generate `impl From<BindingEnum> for core::Enum` with backend-specific config.
pub fn gen_enum_from_binding_to_core_cfg(enum_def: &EnumDef, core_import: &str, config: &ConversionConfig) -> String {
    let core_path = core_enum_path_remapped(enum_def, core_import, config.source_crate_remaps);
    let binding_name = format!("{}{}", config.type_name_prefix, enum_def.name);

    // Arms are emitted unconditionally. The match is on the *binding* enum, which contains
    // exactly `enum_def.variants` (binding-excluded variants are absent). Each variant gets
    // its own arm so the match is exhaustive over the binding type.
    //
    // A core variant's `cfg` is NOT propagated to the binding-side arm: binding crates
    // pull the core dependency with all required features enabled, so any cfg-gated core
    // variant is compiled in when the binding crate is built. Propagating the cfg would
    // reference features that do not exist on the binding crate itself.
    let arms: Vec<minijinja::value::Value> = enum_def
        .variants
        .iter()
        .map(|variant| {
            let arm = binding_to_core_match_arm_ext_cfg(
                &binding_name,
                &variant.name,
                &variant.fields,
                config.binding_enums_have_data,
                config,
                enum_def.serde_untagged && config.binding_tuple_form_for_untagged_variants,
            );
            minijinja::context! {
                arm => arm,
                cfg => Option::<&str>::None,
            }
        })
        .collect();

    crate::codegen::template_env::render(
        "conversions/enum_from_binding_to_core",
        minijinja::context! {
            binding_name => binding_name,
            core_path => core_path,
            arms => arms,
            has_excluded_variants => false,
        },
    )
}

/// Generate `impl From<core::Enum> for BindingEnum` (core -> binding).
pub fn gen_enum_from_core_to_binding(enum_def: &EnumDef, core_import: &str) -> String {
    gen_enum_from_core_to_binding_cfg(enum_def, core_import, &ConversionConfig::default())
}

/// Generate `impl From<core::Enum> for BindingEnum` with backend-specific config.
pub fn gen_enum_from_core_to_binding_cfg(enum_def: &EnumDef, core_import: &str, config: &ConversionConfig) -> String {
    let core_path = core_enum_path_remapped(enum_def, core_import, config.source_crate_remaps);
    let binding_name = format!("{}{}", config.type_name_prefix, enum_def.name);

    // Arms are emitted unconditionally. Binding crates pull the core dependency with all
    // required features enabled, so any cfg-gated core variant is compiled in at the
    // binding-crate build site. Propagating the cfg to the arm would reference features
    // that do not exist on the binding crate itself.
    let arms: Vec<minijinja::value::Value> = enum_def
        .variants
        .iter()
        .map(|variant| {
            let arm = core_to_binding_match_arm_ext_cfg(
                &core_path,
                &variant.name,
                &variant.fields,
                config.binding_enums_have_data,
                config,
                enum_def.serde_untagged && config.binding_tuple_form_for_untagged_variants,
            );
            minijinja::context! {
                arm => arm,
                cfg => Option::<&str>::None,
            }
        })
        .collect();

    // Emit a `_ => Default::default()` catch-all only when the core enum has variants
    // that are excluded from the binding (`excluded_variants`). Those variants are
    // present in the core enum but absent from the binding, so the match needs a
    // wildcard to remain exhaustive.
    //
    // When all core variants are in `enum_def.variants`, the per-variant arms make
    // the match exhaustive and a catch-all would produce an "unreachable pattern"
    // error under `-D warnings`.
    let needs_catch_all = !enum_def.excluded_variants.is_empty();

    crate::codegen::template_env::render(
        "conversions/enum_from_core_to_binding",
        minijinja::context! {
            binding_name => binding_name,
            core_path => core_path,
            arms => arms,
            has_excluded_variants => needs_catch_all,
        },
    )
}
