use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Data, Fields, Ident, Result, Error, LitStr};

/// Parsed content of `#[humanize("template {field} string")]`
struct HumanizeTemplate {
    /// The original template string, e.g. "实例{id}找不到"
    template: String,
    /// Format string with `{field}` replaced by `{}`, e.g. "实例{}找不到"
    format_str: String,
    /// Field names extracted from placeholders, in order
    field_names: Vec<String>,
    /// Span for error reporting
    span: proc_macro2::Span,
}

/// Parse a template string and extract `{field_name}` placeholders.
fn parse_template(lit: &LitStr) -> Result<HumanizeTemplate> {
    let template = lit.value();
    let span = lit.span();
    let mut format_str = String::with_capacity(template.len());
    let mut field_names = Vec::new();

    let mut chars = template.chars().peekable();
    while let Some(ch) = chars.next() {
        if ch == '{' {
            // Collect field name until '}'
            let mut name = String::new();
            loop {
                match chars.next() {
                    Some('}') => break,
                    Some(c) => name.push(c),
                    None => {
                        return Err(Error::new(span, "unclosed '{' in humanize template"));
                    }
                }
            }
            if name.is_empty() {
                return Err(Error::new(span, "empty placeholder '{}' in humanize template — use {field_name}"));
            }
            // Validate field name is a valid identifier
            if !name.chars().next().is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
                || !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
            {
                return Err(Error::new(
                    span,
                    format!("invalid placeholder name '{{{}}}' in humanize template", name),
                ));
            }
            field_names.push(name);
            format_str.push_str("{}");
        } else {
            format_str.push(ch);
        }
    }

    Ok(HumanizeTemplate {
        template,
        format_str,
        field_names,
        span,
    })
}

/// Extract `#[humanize("...")]` attribute from a variant's attributes.
fn extract_humanize(attrs: &[syn::Attribute]) -> Result<Option<HumanizeTemplate>> {
    for attr in attrs {
        if attr.path().is_ident("humanize") {
            let lit: LitStr = attr.parse_args()?;
            let template = parse_template(&lit)?;
            return Ok(Some(template));
        }
    }
    Ok(None)
}

pub fn derive_mad_error(input: DeriveInput) -> TokenStream {
    match derive_mad_error_inner(input) {
        Ok(ts) => ts,
        Err(e) => e.to_compile_error(),
    }
}

fn derive_mad_error_inner(input: DeriveInput) -> Result<TokenStream> {
    let enum_name = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let data = match &input.data {
        Data::Enum(data) => data,
        _ => {
            return Err(Error::new_spanned(
                &input.ident,
                "MadError can only be derived for enums",
            ));
        }
    };

    let mut display_arms = Vec::new();
    let mut humanize_arms = Vec::new();

    for variant in &data.variants {
        let variant_name = &variant.ident;
        let humanize = extract_humanize(&variant.attrs)?.ok_or_else(|| {
            Error::new_spanned(
                variant_name,
                format!(
                    "variant `{}` is missing `#[humanize(\"...\")]` attribute",
                    variant_name
                ),
            )
        })?;

        match &variant.fields {
            Fields::Named(fields_named) => {
                // Validate that all placeholder names exist as field names
                let field_names: Vec<String> = fields_named
                    .named
                    .iter()
                    .filter_map(|f| f.ident.as_ref().map(|i| i.to_string()))
                    .collect();
                for placeholder in &humanize.field_names {
                    if !field_names.contains(placeholder) {
                        return Err(Error::new(
                            humanize.span,
                            format!(
                                "placeholder '{{{}}}' does not match any field in variant `{}` (available: {})",
                                placeholder,
                                variant_name,
                                field_names.join(", ")
                            ),
                        ));
                    }
                }

                let used_fields: Vec<Ident> = humanize
                    .field_names
                    .iter()
                    .map(|name| Ident::new(name, proc_macro2::Span::call_site()))
                    .collect();

                let sealed_expr = if humanize.field_names.is_empty() {
                    let msg = &humanize.template;
                    quote! { mad_hatter::Sealed::new(String::from(#msg)) }
                } else {
                    let fmt = &humanize.format_str;
                    let args: Vec<TokenStream> = used_fields
                        .iter()
                        .map(|f| quote! { &#f })
                        .collect();
                    quote! { mad_hatter::Sealed::new(format!(#fmt, #(#args),*)) }
                };

                // Deduplicate used_fields for destructuring (a field might appear multiple times in template)
                let mut dedup_fields: Vec<&Ident> = Vec::new();
                for f in &used_fields {
                    if !dedup_fields.contains(&f) {
                        dedup_fields.push(f);
                    }
                }

                if dedup_fields.is_empty() {
                    display_arms.push(quote! {
                        Self::#variant_name { .. } => {
                            let __sealed = #sealed_expr;
                            mad_hatter::sealed::__mad_error_display(&__sealed, f)
                        }
                    });
                    humanize_arms.push(quote! {
                        Self::#variant_name { .. } => #sealed_expr
                    });
                } else {
                    display_arms.push(quote! {
                        Self::#variant_name { #(#dedup_fields),*, .. } => {
                            let __sealed = #sealed_expr;
                            mad_hatter::sealed::__mad_error_display(&__sealed, f)
                        }
                    });
                    humanize_arms.push(quote! {
                        Self::#variant_name { #(#dedup_fields),*, .. } => #sealed_expr
                    });
                }
            }
            Fields::Unit => {
                // No fields — placeholders must be empty
                if !humanize.field_names.is_empty() {
                    return Err(Error::new(
                        humanize.span,
                        format!(
                            "unit variant `{}` cannot have placeholders in humanize template",
                            variant_name
                        ),
                    ));
                }

                let msg = &humanize.template;
                let sealed_expr = quote! { mad_hatter::Sealed::new(String::from(#msg)) };

                display_arms.push(quote! {
                    Self::#variant_name => {
                        let __sealed = #sealed_expr;
                        mad_hatter::sealed::__mad_error_display(&__sealed, f)
                    }
                });
                humanize_arms.push(quote! {
                    Self::#variant_name => #sealed_expr
                });
            }
            Fields::Unnamed(_) => {
                return Err(Error::new_spanned(
                    variant_name,
                    format!(
                        "variant `{}` has unnamed (tuple) fields — MadError only supports named fields or unit variants",
                        variant_name
                    ),
                ));
            }
        }
    }

    let expanded = quote! {
        impl #impl_generics std::fmt::Display for #enum_name #ty_generics #where_clause {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                match self {
                    #(#display_arms),*
                }
            }
        }

        impl #impl_generics std::error::Error for #enum_name #ty_generics #where_clause {}

        impl #impl_generics #enum_name #ty_generics #where_clause {
            pub fn humanize(&self) -> mad_hatter::Sealed<String> {
                match self {
                    #(#humanize_arms),*
                }
            }
        }
    };

    Ok(expanded)
}