use proc_macro::TokenStream;
use quote::quote;
use syn::{self};

#[proc_macro_derive(CosmicConfigEntry)]
pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast = syn::parse(input).unwrap();

    // Build the trait implementation
    impl_cosmic_config_entry_macro(&ast)
}

fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    // let generics = &ast.generics;

    // Get the fields of the struct
    let fields = match ast.data {
        syn::Data::Struct(ref data_struct) => match data_struct.fields {
            syn::Fields::Named(ref fields) => &fields.named,
            _ => unimplemented!("Only named fields are supported"),
        },
        _ => unimplemented!("Only structs are supported"),
    };

    let write_each_config_field = fields.iter().map(|field| {
        let field_name = &field.ident;
        quote! {
            cosmic_config::ConfigSet::set(config, stringify!(#field_name), &self.#field_name)?;
        }
    });

    let get_each_config_field = fields.iter().map(|field| {
        let field_name = &field.ident;
        let field_type = &field.ty;
        quote! {
            match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
                Ok(#field_name) => default.#field_name = #field_name,
                Err(e) => errors.push(e),
            }
        }
    });

    // // Get the existing where clause or create a new one if it doesn't exist
    // let mut where_clause = ast
    //     .generics
    //     .where_clause
    //     .clone()
    //     .unwrap_or_else(|| parse_quote!(where));

    // // Add your additional constraints to the where clause
    // // Here, we add the constraint 'T: Debug' to all generic parameters
    // for param in ast.generics.params.iter() {
    //     where_clause
    //         .predicates
    //         .push(parse_quote!(#param: ::std::default::Default + ::serde::Serialize + ::serde::de::DeserializeOwned));
    // }

    let gen = quote! {
        impl CosmicConfigEntry for #name {
            fn write_entry(&self, config: &cosmic_config::Config) -> Result<(), cosmic_config::Error> {
                let tx = config.transaction();
                #(#write_each_config_field)*
                tx.commit()
            }

            fn get_entry(config: &cosmic_config::Config) -> Result<Self, (Vec<cosmic_config::Error>, Self)> {
                let mut default = Self::default();
                let mut errors = Vec::new();

                #(#get_each_config_field)*

                if errors.is_empty() {
                    Ok(default)
                } else {
                    Err((errors, default))
                }
            }
        }
    };

    gen.into()
}
