use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemImpl, Type, parse_macro_input};

use crate::HandlerAttr;

pub fn fusen_handler(attr: HandlerAttr, item: TokenStream) -> TokenStream {
    let org_item = parse_macro_input!(item as ItemImpl);
    let item_self = &org_item.self_ty;
    let id = match attr.id {
        Some(id) => id,
        None => {
            if let Type::Path(path) = item_self.as_ref() {
                path.path.segments[0].ident.to_string()
            } else {
                return syn::Error::new_spanned(org_item, "handler must exist impl")
                    .into_compile_error()
                    .into();
            }
        }
    };
    let item = org_item.clone();
    let Some(trait_ident) = item.trait_.map(|e| e.1) else {
        return syn::Error::new_spanned(org_item, "handler must exist impl")
            .into_compile_error()
            .into();
    };
    let (handler_invoker, handler_trait) = match trait_ident.segments[0].ident.to_string().as_str()
    {
        "LoadBalance" => (
            quote!(fusen_rs::handler::HandlerInvoker::LoadBalance(
                std::sync::Arc::new(Box::new(self))
            ),),
            quote! {
                impl fusen_rs::handler::loadbalance::LoadBalance_ for #item_self {
                    fn select_<'a>(
                        &'a self,
                        context: &'a fusen_rs::protocol::fusen::context::FusenContext,
                        invokers: std::sync::Arc<Vec<std::sync::Arc<fusen_rs::fusen_internal_common::resource::service::ServiceResource>>>,
                    ) -> fusen_rs::fusen_internal_common::BoxFutureV2<'a,Result<std::option::Option<std::sync::Arc<fusen_rs::fusen_internal_common::resource::service::ServiceResource>>, fusen_rs::error::FusenError>> {
                        Box::pin(async move {
                           self.select(context,invokers).await
                        })
                    }
                }
            },
        ),
        "Aspect" => (
            quote!(fusen_rs::handler::HandlerInvoker::Aspect(
                std::sync::Arc::new(Box::new(self))
            ),),
            quote! {
                impl fusen_rs::filter::FusenFilter for #item_self {
                    fn call<'a>(
                        &'a self,
                        join_point: fusen_rs::filter::ProceedingJoinPoint,
                    ) -> fusen_rs::fusen_internal_common::BoxFutureV2<'a,Result<fusen_rs::protocol::fusen::context::FusenContext, fusen_rs::error::FusenError>> {
                        Box::pin(async move {
                            self.aroud(join_point).await
                        })
                    }
                }
            },
        ),
        _ => {
            return syn::Error::new_spanned(
                trait_ident,
                "handler must impl 'LoadBalance', 'Aspect'",
            )
            .into_compile_error()
            .into();
        }
    };
    quote!(
        #org_item

        #handler_trait

        impl fusen_rs::handler::HandlerLoad for #item_self {
            fn load(self) -> fusen_rs::handler::Handler {
                fusen_rs::handler::Handler{
                    id: #id.to_string(),
                    handler_invoker: #handler_invoker
                }
            }
        }
    )
    .into()
}
