//! Code generation helpers

use proc_macro2::TokenStream;
use quote::quote;

use crate::ir::{RegularVariantDefinition, TransparentVariantDefinition, VariantDefinition};

/// Build a pattern for destructuring a regular variant
pub fn build_variant_pattern(variant: &RegularVariantDefinition) -> TokenStream {
    if variant.fields.is_empty() {
        // Unit variant
        quote! {}
    } else {
        // Named fields variant
        let field_names: Vec<_> = variant.fields.iter().map(|f| &f.rust_name).collect();

        quote! {
            { #(#field_names),* }
        }
    }
}

/// Build a pattern for destructuring a transparent variant
pub fn build_transparent_pattern(_variant: &TransparentVariantDefinition) -> TokenStream {
    quote! { (inner) }
}

/// Generate match arms for regular variants using a closure for the body
pub fn generate_regular_match_arms<F>(
    variants: &[VariantDefinition],
    body_fn: F,
) -> Vec<TokenStream>
where
    F: Fn(&RegularVariantDefinition, &TokenStream) -> TokenStream,
{
    variants
        .iter()
        .filter_map(|v| match v {
            VariantDefinition::Regular(regular) => Some(regular),
            VariantDefinition::Transparent(_) => None,
        })
        .map(|variant| {
            let variant_name = &variant.name;
            let pattern = build_variant_pattern(variant);
            let body = body_fn(variant, &pattern);

            quote! {
                Self::#variant_name #pattern => { #body }
            }
        })
        .collect()
}

/// Generate match arms for transparent variants using a closure for the body
pub fn generate_transparent_match_arms<F>(
    variants: &[VariantDefinition],
    body_fn: F,
) -> Vec<TokenStream>
where
    F: Fn(&TransparentVariantDefinition, &TokenStream) -> TokenStream,
{
    variants
        .iter()
        .filter_map(|v| match v {
            VariantDefinition::Regular(_) => None,
            VariantDefinition::Transparent(transparent) => Some(transparent),
        })
        .map(|variant| {
            let variant_name = &variant.name;
            let pattern = build_transparent_pattern(variant);
            let body = body_fn(variant, &pattern);

            quote! {
                Self::#variant_name #pattern => { #body }
            }
        })
        .collect()
}
