//! std::error::Error trait implementation generation

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

use super::helpers::{build_transparent_pattern, build_variant_pattern};
use crate::ir::{EnumDefinition, VariantDefinition};

/// Generate the std::error::Error trait implementation
pub fn generate_error_impl(ir: &EnumDefinition) -> TokenStream {
    let name = &ir.name;
    let (impl_generics, ty_generics, where_clause) = ir.generics.split_for_impl();

    // Generate match arms for source() method
    let source_arms: Vec<TokenStream> = ir
        .variants
        .iter()
        .flat_map(|variant| {
            match variant {
                VariantDefinition::Regular(regular) => {
                    let variant_name = &regular.name;
                    let pattern = build_variant_pattern(regular);

                    // Find field marked with #[source]
                    let source_field = regular.fields.iter().find(|f| f.is_source);

                    let arm = if let Some(field) = source_field {
                        let field_name = &field.rust_name;
                        quote! {
                            Self::#variant_name #pattern => {
                                {
                                    use ::apollo_errors::private::AsDynError as _;
                                    ::std::option::Option::Some(#field_name.as_dyn_error())
                                }
                            }
                        }
                    } else {
                        quote! {
                            Self::#variant_name #pattern => {
                                ::std::option::Option::None
                            }
                        }
                    };
                    vec![arm]
                }
                VariantDefinition::Transparent(transparent) => {
                    // For transparent variants, the inner error IS the source
                    let variant_name = &transparent.name;
                    let pattern = build_transparent_pattern(transparent);
                    vec![quote! {
                        Self::#variant_name #pattern => {
                            ::std::option::Option::Some(inner as &(dyn ::std::error::Error + 'static))
                        }
                    }]
                }
            }
        })
        .collect();

    quote! {
        #[automatically_derived]
        impl #impl_generics ::std::error::Error for #name #ty_generics #where_clause {
            fn source(&self) -> ::std::option::Option<&(dyn ::std::error::Error + 'static)> {
                match self {
                    #(#source_arms)*
                }
            }
        }
    }
}
