use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Meta, Token, parse_macro_input};

pub(crate) mod check_module;
pub(crate) mod field_parser;

mod enum_parser;

#[cfg(feature = "serde")]
mod skip_serializing;

/// Attribute macro that adds `#[serde(skip_serializing_if = "...")]` to every
/// field of a struct, skipping fields whose value equals their default state.
///
/// Fields annotated with `#[defaulted(default = expr)]` also receive a
/// matching `#[serde(default = "...")]` so they are filled in correctly during
/// deserialization when the key is absent.
///
/// # Usage
///
/// Place `#[skip_serializing_defaults]` *before* `#[derive(...)]` on a struct
/// with named fields.  The struct must implement (or derive) `Serialize`.
///
/// ```rust,ignore
/// use defaulted::Defaulted;
/// use serde::Serialize;
///
/// #[defaulted::skip_serializing_defaults]
/// #[derive(Serialize, Defaulted)]
/// #[defaulted(Default)]
/// struct Config {
///     #[defaulted(default = "dark")]
///     theme: String,
///
///     #[defaulted(default = 8080)]
///     port: u16,
///
///     tags: Vec<String>,
/// }
///
/// let c = Config::default();
/// assert_eq!(serde_json::to_string(&c).unwrap(), "{}");
/// ```
///
/// The macro works with or without `#[derive(Defaulted)]`.  When used alone,
/// `#[defaulted(...)]` field attributes are stripped after processing so they
/// do not cause compiler errors.
///
/// # Constraints
///
/// - Only structs with named fields are supported.
/// - Fields that already carry `#[serde(skip_serializing_if = "...")]` are
///   left untouched.
/// - Fields marked `#[defaulted(ignore)]` are always serialized.
#[cfg(feature = "serde")]
#[proc_macro_attribute]
pub fn skip_serializing_defaults(attr: TokenStream, item: TokenStream) -> TokenStream
{
    skip_serializing::apply(attr, item)
}

/// Derive macro for [`defaulted::Defaulted`](https://docs.rs/defaulted).
///
/// Generates an `Defaulted` implementation for a struct or enum.  By default
/// each field delegates to its own `Defaulted` impl; per-field attributes allow
/// overriding that behaviour.
///
/// # Per-field attributes
///
/// All attributes use the `#[defaulted(...)]` namespace.
///
/// ## `#[defaulted(ignore)]`
///
/// Skip this field entirely -- it never affects the result.
///
/// ```rust,ignore
/// use defaulted::Defaulted;
///
/// #[derive(Defaulted)]
/// struct Foo {
///     value: u32,
///     #[defaulted(ignore)]
///     _id: u64,
/// }
/// ```
///
/// ## `#[defaulted(default = expr)]`
///
/// The field is "default" when it equals `expr`.
///
/// ```rust,ignore
/// use defaulted::Defaulted;
///
/// #[derive(Defaulted)]
/// struct Timeout {
///     #[defaulted(default = 30)]
///     secs: u64,
/// }
/// ```
///
/// ## `#[defaulted(with = func)]` / `#[defaulted(with = |v| ...)]`
///
/// Call a function or closure with `&field`; `true` means "at default".
///
/// ```rust,ignore
/// use defaulted::Defaulted;
///
/// fn is_short(s: &str) -> bool { s.len() < 5 }
///
/// #[derive(Defaulted)]
/// struct Label {
///     #[defaulted(with = is_short)]
///     name: String,
/// }
/// ```
///
/// # Struct-level attributes
///
/// ## `#[defaulted(Default)]`
///
/// Also generate a [`Default`] impl whose field values come from the
/// `#[defaulted(default = expr)]` annotations.  Fields without an explicit
/// default fall back to `<FieldType as Default>::default()`.
///
/// ```rust,ignore
/// use defaulted::Defaulted;
///
/// #[derive(Debug, PartialEq, Defaulted)]
/// #[defaulted(Default)]
/// struct Settings {
///     #[defaulted(default = 8080)]
///     port: u16,
///     label: String,
/// }
///
/// let s = Settings::default();
/// assert_eq!(s.port, 8080);
/// assert!(s.is_defaulted());
/// ```
///
/// # Enums
///
/// Mark exactly one variant with `#[defaulted(default)]`.  The struct-level
/// field attributes work on enum variant fields as well.
///
/// ```rust,ignore
/// use defaulted::Defaulted;
///
/// #[derive(Defaulted)]
/// enum State {
///     #[defaulted(default)]
///     Idle,
///     Running,
/// }
///
/// assert!(State::Idle.is_defaulted());
/// assert!(!State::Running.is_defaulted());
/// ```
///
/// # Generics
///
/// The macro automatically infers `where T: Defaulted` bounds for type
/// parameters that appear in fields delegating to `Defaulted::is_defaulted()`.
/// Fields using `with`, `default`, or `ignore` do not contribute bounds.
#[proc_macro_derive(Defaulted, attributes(defaulted))]
pub fn derive_defaulted(input: TokenStream) -> TokenStream
{
    let input = parse_macro_input!(input as DeriveInput);
    match derive_defaulted_inner(&input) {
        Ok(tokens) => tokens.into(),
        Err(e) => e.into_compile_error().into(),
    }
}

fn derive_defaulted_inner(input: &DeriveInput) -> syn::Result<TokenStream2>
{
    let name = &input.ident;
    let generics = &input.generics;
    let struct_attrs: StructAttrs = input.attrs.as_slice().parse_attrs()?;

    let defaulted_impl = match &input.data {
        #[cfg(feature = "serde")]
        Data::Struct(s) if struct_attrs.has_check_module => {
            // Attribute macro already created the companion module;
            // call into its Helper functions.
            check_module::generate_defaulted_body(name, generics, &s.fields)?
        }
        Data::Struct(s) => {
            // Inline the checks directly to avoid `proc_macro_derive_resolution_fallback`
            // if the struct is defined inside a function scope.
            check_module::generate_inline_defaulted_body(&s.fields)?
        }
        Data::Enum(e) => enum_parser::generate(e.clone()),
        Data::Union(_) => {
            return Err(syn::Error::new_spanned(
                name,
                "Defaulted cannot be derived for unions",
            ));
        }
    };

    // Auto-add `where T: Defaulted` for type params in Delegate fields.
    let defaulted_generics = match &input.data {
        Data::Struct(s) => {
            let preds = check_module::defaulted_predicates(generics, &s.fields)?;
            check_module::augment_generics(generics, &preds)
        }
        _ => generics.clone(),
    };
    let (impl_generics, ty_generics, where_clause) = defaulted_generics.split_for_impl();

    let default_impl = if struct_attrs.impl_default {
        match &input.data {
            Data::Struct(s) => Some(generate_default_impl(name, generics, &s.fields)?),
            _ => {
                return Err(syn::Error::new_spanned(
                    name,
                    "defaulted(Default) is only supported on structs",
                ));
            }
        }
    } else {
        None
    };

    Ok(quote! {
        impl #impl_generics ::defaulted::Defaulted for #name #ty_generics #where_clause {
            fn is_defaulted(&self) -> bool {
                #defaulted_impl
            }
        }
        #default_impl
    })
}

/// Generate a `Default` impl from `#[defaulted(default = expr)]` field
/// attributes. Fields without the attribute use their type's `Default`.
fn generate_default_impl(
    name: &syn::Ident,
    generics: &syn::Generics,
    fields: &syn::Fields,
) -> syn::Result<TokenStream2>
{
    // Auto-add `where T: Default` for type params in fields without
    // explicit `#[defaulted(default = ...)]` values.
    let preds = check_module::default_predicates(generics, fields)?;
    let augmented = check_module::augment_generics(generics, &preds);
    let (impl_generics, ty_generics, where_clause) = augmented.split_for_impl();

    let field_defaults: Vec<TokenStream2> = fields
        .iter()
        .map(field_default_expr)
        .collect::<syn::Result<_>>()?;

    Ok(quote! {
        impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause {
            fn default() -> Self {
                Self {
                    #(#field_defaults,)*
                }
            }
        }
    })
}

/// Build `field_name: <expr>` for a single field inside the Default impl.
fn field_default_expr(field: &syn::Field) -> syn::Result<TokenStream2>
{
    let field_name = &field.ident;
    let attrs: field_parser::FieldAttrs = field.parse_attrs()?;

    Ok(attrs.checker.default_value_expr(field.span()).map_or_else(
        || {
            let ty = &field.ty;
            quote_spanned! {field.span()=> #field_name: <#ty as ::core::default::Default>::default() }
        },
        |expr| quote_spanned! {field.span()=> #field_name: #expr },
    ))
}

/// Struct-level attributes parsed from `#[defaulted(...)]`.
#[derive(Default)]
struct StructAttrs
{
    impl_default:     bool,
    has_check_module: bool,
}

impl ParseAttrs<StructAttrs> for &syn::Attribute
{
    fn parse_attrs_into(self, attrs: &mut StructAttrs) -> syn::Result<()>
    {
        if self.path().is_ident(stringify!(defaulted)) {
            let meta = self.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
            meta.parse_attrs_into(attrs)?;
        }
        Ok(())
    }
}

impl ParseAttrs<StructAttrs> for Meta
{
    fn parse_attrs_into(self, attrs: &mut StructAttrs) -> syn::Result<()>
    {
        match &self {
            Self::Path(path) if path.is_ident("Default") => {
                attrs.impl_default = true;
                Ok(())
            }
            Self::Path(path) if path.is_ident("__has_check_module") => {
                attrs.has_check_module = true;
                Ok(())
            }
            _ => Ok(()),
        }
    }
}

trait ParseAttrs<Attrs>
{
    fn parse_attrs_into(self, attrs: &mut Attrs) -> syn::Result<()>;
    fn parse_attrs(self) -> syn::Result<Attrs>
    where
        Attrs: Default,
        Self: Sized,
    {
        let mut attrs = Attrs::default();
        self.parse_attrs_into(&mut attrs)?;
        Ok(attrs)
    }
}
impl<T, A> ParseAttrs<A> for Option<T>
where
    T: ParseAttrs<A>,
{
    fn parse_attrs_into(self, attrs: &mut A) -> syn::Result<()>
    {
        if let Some(parser) = self {
            parser.parse_attrs_into(attrs)?;
        }
        Ok(())
    }
}
impl<T, A> ParseAttrs<A> for Vec<T>
where
    T: ParseAttrs<A>,
{
    fn parse_attrs_into(self, attrs: &mut A) -> syn::Result<()>
    {
        for item in self {
            item.parse_attrs_into(attrs)?;
        }
        Ok(())
    }
}
impl<'a, T, A> ParseAttrs<A> for &'a [T]
where
    &'a T: ParseAttrs<A>,
{
    fn parse_attrs_into(self, attrs: &mut A) -> syn::Result<()>
    {
        for item in self {
            item.parse_attrs_into(attrs)?;
        }
        Ok(())
    }
}
impl<A, P> ParseAttrs<A> for Punctuated<Meta, P>
where
    Meta: ParseAttrs<A>,
{
    fn parse_attrs_into(self, attrs: &mut A) -> syn::Result<()>
    {
        for item in self {
            item.parse_attrs_into(attrs)?;
        }
        Ok(())
    }
}
