use quote::quote;
use syn::parse::Parser;

pub struct HoneycombContextFlags {
    pub global: bool,
    pub compression_program: bool,
    pub noop_program: bool,
}

pub struct HoneycombContext(syn::ItemStruct, HoneycombContextFlags);

impl HoneycombContext {
    pub fn new(input: syn::ItemStruct, flags: HoneycombContextFlags) -> Self {
        Self(input, flags)
    }
}

impl HoneycombContext {
    pub fn render(self) -> proc_macro::TokenStream {
        let context_struct = self.0;
        let context_ident = context_struct.ident;
        let context_generics = context_struct.generics;

        let mut reconstructed_struct: syn::ItemStruct =
            syn::parse(proc_macro::TokenStream::from({
                quote! {
                    pub struct #context_ident #context_generics {
                        #[account(mut)]
                        pub project: Account<'info, hpl_hive_control::state::Project>,
                    }
                }
            }))
            .unwrap();

        context_struct
            .attrs
            .iter()
            .for_each(|a| reconstructed_struct.attrs.push(a.clone()));

        if let syn::Fields::Named(fields) = &context_struct.fields {
            if let syn::Fields::Named(reconstructed_field) = &mut reconstructed_struct.fields {
                if self.1.global {
                    reconstructed_field.named.push(
                        syn::Field::parse_named
                            .parse2(quote! {
                                pub global: Account<'info,hpl_hive_control::state:: Global>
                            })
                            .unwrap(),
                    );
                }

                fields.named.iter().for_each(|f| {
                    reconstructed_field.named.push(f.clone());
                });

                reconstructed_field.named.push(
                    syn::Field::parse_named
                        .parse2(quote! {
                            #[account(mut)]
                            pub payer: Signer<'info>
                        })
                        .unwrap(),
                );

                reconstructed_field.named.push(
                    syn::Field::parse_named
                        .parse2(quote! {
                            #[account(mut)]
                            pub vault: AccountInfo<'info>
                        })
                        .unwrap(),
                );

                reconstructed_field.named.push(
                    syn::Field::parse_named
                        .parse2(quote! {
                            pub system_program: Program<'info, System>
                        })
                        .unwrap(),
                );

                reconstructed_field.named.push(
                    syn::Field::parse_named
                        .parse2(quote! {
                            pub hive_control: Program<'info, hpl_hive_control::program::HplHiveControl>
                        })
                        .unwrap(),
                );

                if self.1.compression_program {
                    reconstructed_field.named.push(
                        syn::Field::parse_named
                            .parse2(quote! {
                                pub compression_program: Program<'info, spl_account_compression::program::SplAccountCompression>
                            })
                            .unwrap(),
                    );
                }

                if self.1.noop_program {
                    reconstructed_field.named.push(
                        syn::Field::parse_named
                            .parse2(quote! {
                                pub log_wrapper: Program<'info, spl_account_compression::Noop>
                            })
                            .unwrap(),
                    );
                }

                reconstructed_field.named.push(
                    syn::Field::parse_named
                        .parse2(quote! {
                            pub instructions_sysvar: AccountInfo<'info>
                        })
                        .unwrap(),
                );
            }
        } else {
            panic!("Cannot inject accounts in unnamed fields")
        }

        let stream = quote! {
            #[derive(Accounts)]
            #reconstructed_struct
        };

        stream.into()
    }
}
