use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed};

/// Expand `#[derive(FromRow)]` for structs with named fields.
///
/// This is a simplified replacement for `sqlx::FromRow` that generates
/// paths through `::mad_hatter::__sqlx::` instead of `::sqlx::`,
/// so engine crates don't need sqlx as a direct dependency.
///
/// Supports only named-field structs with no special attributes.
/// Advanced sqlx features (rename, skip, flatten, json, try_from, default)
/// are not supported — use the framework's own persistence abstractions instead.
pub fn expand_derive_from_row(input: &DeriveInput) -> syn::Result<TokenStream> {
    match &input.data {
        Data::Struct(DataStruct {
            fields: Fields::Named(FieldsNamed { named, .. }),
            ..
        }) => {
            let ident = &input.ident;
            let (_, ty_generics, _) = input.generics.split_for_impl();

            // Collect field names and types
            let mut field_idents = Vec::new();
            let mut field_name_strs = Vec::new();
            let mut field_types = Vec::new();

            for field in named {
                let field_ident = field.ident.as_ref().ok_or_else(|| {
                    syn::Error::new_spanned(field, "expected a named field")
                })?;
                field_idents.push(field_ident.clone());
                field_name_strs.push(field_ident.to_string());
                field_types.push(field.ty.clone());
            }

            // Build generics: add lifetime 'a and R: Row
            let mut generics = input.generics.clone();

            // Add lifetime if not already present
            let has_lifetime = generics.lifetimes().next().is_some();
            let lifetime: syn::Lifetime = if has_lifetime {
                generics.lifetimes().next().unwrap().lifetime.clone()
            } else {
                let lt = syn::Lifetime::new("'a", proc_macro2::Span::call_site());
                generics
                    .params
                    .insert(0, syn::GenericParam::Lifetime(syn::LifetimeParam::new(lt.clone())));
                lt
            };

            // Add R: ::mad_hatter::__sqlx::Row
            generics
                .params
                .push(syn::parse_quote!(R: ::mad_hatter::__sqlx::Row));

            // Build where clause
            let predicates = &mut generics.make_where_clause().predicates;

            // &'a str: ColumnIndex<R>
            predicates.push(syn::parse_quote!(
                &#lifetime ::std::primitive::str: ::mad_hatter::__sqlx::ColumnIndex<R>
            ));

            // For each field: T: Decode<'a, R::Database> + Type<R::Database>
            for ty in &field_types {
                predicates.push(syn::parse_quote!(
                    #ty: ::mad_hatter::__sqlx::Decode<#lifetime, <R as ::mad_hatter::__sqlx::Row>::Database>
                ));
                predicates.push(syn::parse_quote!(
                    #ty: ::mad_hatter::__sqlx::Type<<R as ::mad_hatter::__sqlx::Row>::Database>
                ));
            }

            let (impl_generics, _, where_clause) = generics.split_for_impl();

            Ok(quote! {
                #[automatically_derived]
                impl #impl_generics ::mad_hatter::__sqlx::FromRow<#lifetime, R> for #ident #ty_generics #where_clause {
                    fn from_row(__row: &#lifetime R) -> ::std::result::Result<Self, ::mad_hatter::__sqlx::Error> {
                        ::std::result::Result::Ok(Self {
                            #(
                                #field_idents: __row.try_get(#field_name_strs)?,
                            )*
                        })
                    }
                }
            })
        }
        _ => Err(syn::Error::new_spanned(
            input,
            "FromRow can only be derived for structs with named fields",
        )),
    }
}