extern crate proc_macro;

use proc_macro::TokenStream;
use quote::{quote, format_ident};
use syn::{
    parse::Parse, parse::ParseStream, parse_macro_input, Attribute, FnArg, Ident, ItemFn, LitStr,
    Pat, Result, ReturnType, Token, Type
};


// --- Macro Implementations ---

fn unwrap_pat_paren(pat: &Pat) -> &Pat {
    if let Pat::Paren(pp) = pat {
        unwrap_pat_paren(&pp.pat)
    } else {
        pat
    }
}

struct ApiMacroArgs {
    method: Ident,
    path: LitStr,
    responses: Vec<(syn::LitInt, LitStr, Option<Type>)>,
    output: Option<(Ident, LitStr)>, // Some( (Detail|List), type_name_literal )
}

impl Parse for ApiMacroArgs {
    fn parse(input: ParseStream) -> Result<Self> {
        let method: Ident = input.parse()?;
        input.parse::<Token![,]>()?;
        let path: LitStr = input.parse()?;

        let mut responses: Vec<(syn::LitInt, LitStr, Option<Type>)> = Vec::new();
        let mut output: Option<(Ident, LitStr)> = None;

        // Parse optional trailing args separated by commas: output(detail|list = "Type"), responses(...)
        while input.parse::<Option<Token![,]>>()?.is_some() {
            if input.peek(Ident) {
                let kw: Ident = input.parse()?;
                let kw_str = kw.to_string();
                if kw_str == "responses" {
                    let content;
                    syn::parenthesized!(content in input);
                    while !content.is_empty() {
                        let group;
                        syn::parenthesized!(group in content);
                        let code: syn::LitInt = group.parse()?;
                        group.parse::<Token![,]>()?;
                        let desc: LitStr = group.parse()?;
                        let ty: Option<Type> = if group.parse::<Option<Token![,]>>()?.is_some() {
                            Some(group.parse()?)
                        } else { None };
                        responses.push((code, desc, ty));
                        let _ = content.parse::<Option<Token![,]>>()?;
                    }
                } else if kw_str == "output" {
                    let content;
                    syn::parenthesized!(content in input);
                    let kind: Ident = content.parse()?; // detail | list
                    content.parse::<Token![=]>()?;
                    let ty_lit: LitStr = content.parse()?; // "TypeName"
                    output = Some((kind, ty_lit));
                } else {
                    // Unknown: stop parsing further
                    break;
                }
            } else {
                break;
            }
        }

        Ok(ApiMacroArgs { method, path, responses, output })
    }
}

#[proc_macro_attribute]
pub fn api(args: TokenStream, input: TokenStream) -> TokenStream {
    let item_fn = parse_macro_input!(input as ItemFn);
    let args_parsed = parse_macro_input!(args as ApiMacroArgs);

    let fn_ident = &item_fn.sig.ident;
    let fn_name_str = fn_ident.to_string();
    let method_str = args_parsed.method.to_string();
    let path_str = args_parsed.path.value();
    let (summary, description) = parse_doc_comments(&item_fn.attrs);

    // --- Parse Parameters and Request Body ---
    let mut params_tokens = Vec::new();
    let mut request_body_token = quote! { None };
    let mut _needs_header_map: bool = false;
    let mut _needs_state: bool = false;
    let mut dto_inventory_tokens: Vec<proc_macro2::TokenStream> = Vec::new();

    // For building runtime wrapper
    let mut arg_prepare_tokens = Vec::new();
    let mut call_args_tokens = Vec::new();
    let mut _state_type_tokens: Option<proc_macro2::TokenStream> = None;

    for arg in &item_fn.sig.inputs {
        if let FnArg::Typed(pat_type) = arg {
            if let Some(inner_type) = get_inner_type(&pat_type.ty, "Path") {
                if let Pat::TupleStruct(pat_tuple) = &*pat_type.pat {
                    // FIX: Support Pat::Paren unwrapping and unified handling for Ident/Tuple
                    if let Some(first_pat) = pat_tuple.elems.first() {
                       let inner_pat = unwrap_pat_paren(first_pat);

                       if let Pat::Ident(inner_pat) = inner_pat {
                            let param_name = inner_pat.ident.to_string();
                            let type_name = type_to_string(inner_type);
                            let type_name_s = sanitize_component_name(&type_name);
                            params_tokens.push(quote! {
                                ::service_kit::ApiParameter {
                                    name: #param_name,
                                    param_in: ::service_kit::ParamIn::Path,
                                    description: "", // TODO: Parse from attributes
                                    required: true,
                                    type_name: #type_name_s,
                                }
                            });
                            // runtime wrapper: read string and wrap
                            let var_ident = &inner_pat.ident;
                            let inner_ty_tokens = quote! { #inner_type };
                            arg_prepare_tokens.push(quote! {
                                let #var_ident: #inner_ty_tokens = match params.get(#param_name).and_then(|v| v.as_str()) {
                                    Some(s) => match s.parse::<#inner_ty_tokens>() {
                                        Ok(v) => v,
                                        Err(_) => return Err(::service_kit::error::Error::SpecError(format!("invalid path param `{}`", #param_name))),
                                    },
                                    None => return Err(::service_kit::error::Error::SpecError(format!("missing path param `{}`", #param_name))),
                                };
                                let #var_ident = axum::extract::Path::<#inner_ty_tokens>(#var_ident);
                            });
                            call_args_tokens.push(quote! { #var_ident });

                            // auto-register DTO for Path<T>
                            let type_name = type_to_string(inner_type);
                            let type_name_s = sanitize_component_name(&type_name);
                            dto_inventory_tokens.push(quote! {
                                ::service_kit::inventory::submit! {
                                    ::service_kit::ApiDtoMetadata {
                                        name: #type_name_s,
                                        schema_provider: || (
                                            #type_name_s.to_string(),
                                            <#inner_type as ::service_kit::utoipa::PartialSchema>::schema(),
                                        ),
                                    }
                                }
                            });
                       } else if let Pat::Tuple(tuple_pat) = inner_pat {
                            // Handle Path((a, b)) pattern
                            let inner_tuple_types = if let Type::Tuple(t) = inner_type {
                               &t.elems
                            } else if let Type::Paren(tp) = inner_type {
                               if let Type::Tuple(t) = &*tp.elem {
                                   &t.elems
                               } else {
                                   panic!("Path extractor pattern is a tuple but type is not a tuple (unwrapped paren): {:?}", type_to_string(inner_type));
                               }
                            } else {
                               panic!("Path extractor pattern is a tuple but type is not a tuple: {:?}", type_to_string(inner_type));
                            };

                            if tuple_pat.elems.len() != inner_tuple_types.len() {
                                 panic!("Path extractor tuple pattern length mismatch with type tuple length");
                            }

                            // Extract each element
                            let mut extracted_vars = Vec::new();
                            for (_idx, (elem_pat, elem_ty)) in tuple_pat.elems.iter().zip(inner_tuple_types.iter()).enumerate() {
                                 if let Pat::Ident(pat_ident) = elem_pat {
                                     let param_name = pat_ident.ident.to_string();
                                     let type_name = type_to_string(elem_ty);
                                     let type_name_s = sanitize_component_name(&type_name);
                                     
                                     params_tokens.push(quote! {
                                         ::service_kit::ApiParameter {
                                             name: #param_name,
                                             param_in: ::service_kit::ParamIn::Path,
                                             description: "",
                                             required: true,
                                             type_name: #type_name_s,
                                         }
                                     });
                                     
                                     // No individual arg preparation here, we do it in bulk below or just note the vars
                                     extracted_vars.push((pat_ident.ident.clone(), elem_ty.clone(), param_name));
                                 } else {
                                     panic!("Unsupported pattern in Path tuple");
                                 }
                            }
                            
                            // Runtime wrapper: extract all vars and reconstruct the tuple
                            let tuple_reconstruct_tokens: Vec<_> = extracted_vars.iter().map(|(ident, _, _)| quote! { #ident }).collect();
                            
                            for (ident, ty, param_name) in &extracted_vars {
                                let ty_tokens = quote! { #ty };
                                arg_prepare_tokens.push(quote! {
                                    let #ident: #ty_tokens = match params.get(#param_name).and_then(|v| v.as_str()) {
                                        Some(s) => match s.parse::<#ty_tokens>() {
                                            Ok(v) => v,
                                            Err(_) => return Err(::service_kit::error::Error::SpecError(format!("invalid path param `{}`", #param_name))),
                                        },
                                        None => return Err(::service_kit::error::Error::SpecError(format!("missing path param `{}`", #param_name))),
                                    };
                                });
                                 // Register DTO for each simple type
                                 let type_name = type_to_string(ty);
                                 let type_name_s = sanitize_component_name(&type_name);
                                 dto_inventory_tokens.push(quote! {
                                     ::service_kit::inventory::submit! {
                                         ::service_kit::ApiDtoMetadata {
                                             name: #type_name_s,
                                             schema_provider: || (
                                                 #type_name_s.to_string(),
                                                 <#ty as ::service_kit::utoipa::PartialSchema>::schema(),
                                             ),
                                         }
                                     }
                                 });
                            }
                            
                            let tuple_ty_tokens = quote! { #inner_type };
                            arg_prepare_tokens.push(quote! {
                                 let tuple_arg = axum::extract::Path::<#tuple_ty_tokens>(( #(#tuple_reconstruct_tokens),* ));
                            });
                            call_args_tokens.push(quote! { tuple_arg });
                       }
                    }
                } else if let Pat::Ident(pat_ident) = &*pat_type.pat {
                    // Also support `id: Path<String>` style
                    let param_name = pat_ident.ident.to_string();
                    let type_name = type_to_string(inner_type);
                    let type_name_s = sanitize_component_name(&type_name);
                    params_tokens.push(quote! {
                        ::service_kit::ApiParameter {
                            name: #param_name,
                            param_in: ::service_kit::ParamIn::Path,
                            description: "",
                            required: true,
                            type_name: #type_name_s,
                        }
                    });
                    let var_ident = &pat_ident.ident;
                    let inner_ty_tokens = quote! { #inner_type };
                    arg_prepare_tokens.push(quote! {
                        let #var_ident: #inner_ty_tokens = match params.get(#param_name).and_then(|v| v.as_str()) {
                            Some(s) => match s.parse::<#inner_ty_tokens>() {
                                Ok(v) => v,
                                Err(_) => return Err(::service_kit::error::Error::SpecError(format!("invalid path param `{}`", #param_name))),
                            },
                            None => return Err(::service_kit::error::Error::SpecError(format!("missing path param `{}`", #param_name))),
                        };
                        let #var_ident = axum::extract::Path::<#inner_ty_tokens>(#var_ident);
                    });
                    call_args_tokens.push(quote! { #var_ident });

                    // auto-register DTO for Path<T>
                    let type_name = type_to_string(inner_type);
                    let type_name_s = sanitize_component_name(&type_name);
                    dto_inventory_tokens.push(quote! {
                        ::service_kit::inventory::submit! {
                            ::service_kit::ApiDtoMetadata {
                                name: #type_name_s,
                                schema_provider: || (
                                    #type_name_s.to_string(),
                                    <#inner_type as ::service_kit::utoipa::PartialSchema>::schema(),
                                ),
                            }
                        }
                    });
                }
            } else if let Some(inner_type) = get_inner_type(&pat_type.ty, "Query") {
                // Support both `Query(params): Query<T>` and `params: Query<T>` patterns
                let param_name_opt = if let Pat::TupleStruct(pat_tuple) = &*pat_type.pat {
                    pat_tuple
                        .elems
                        .first()
                        .and_then(|p| match p { Pat::Ident(pi) => Some(pi.ident.to_string()), _ => None })
                } else if let Pat::Ident(pat_ident) = &*pat_type.pat {
                    Some(pat_ident.ident.to_string())
                } else { None };

                if let Some(param_name) = param_name_opt {
                    let type_name = type_to_string(inner_type);
                    let type_name_s = sanitize_component_name(&type_name);
                    // 跳过在 OpenAPI 中暴露 AuthQuery（避免把 authorization 暴露为查询参数）
                    if type_name != "AuthQuery" && !type_name.ends_with("::AuthQuery") {
                        params_tokens.push(quote! {
                            ::service_kit::ApiParameter {
                                name: #param_name,
                                param_in: ::service_kit::ParamIn::Query,
                                description: "", // TODO: Parse from attributes
                                required: true, // TODO: Detect Option
                                type_name: #type_name_s,
                            }
                        });
                    }
                    // runtime wrapper: deserialize whole params into T
                    let var_ident = format_ident!("{}", param_name);
                     let inner_ty_tokens = quote! { #inner_type };
                    arg_prepare_tokens.push(quote! {
                        let #var_ident: #inner_ty_tokens = match __raw_query_str.as_deref() {
                            Some(s) if !s.is_empty() => match ::service_kit::serde_urlencoded::from_str::<#inner_ty_tokens>(s) {
                                Ok(v) => v,
                                Err(e) => return Err(::service_kit::error::Error::SerdeUrlencoded(e)),
                            },
                            _ => match ::service_kit::serde_urlencoded::from_str::<#inner_ty_tokens>("") {
                                Ok(v) => v,
                                Err(e) => return Err(::service_kit::error::Error::SerdeUrlencoded(e)),
                            }
                        };
                        let #var_ident = axum::extract::Query::<#inner_ty_tokens>(#var_ident);
                    });
                    call_args_tokens.push(quote! { #var_ident });

                    // auto-register DTO for Query<T>, except AuthQuery to avoid exposing authorization in OpenAPI
                    let type_name = type_to_string(inner_type);
                    let type_name_s = sanitize_component_name(&type_name);
                    if type_name != "AuthQuery" && !type_name.ends_with("::AuthQuery") {
                        dto_inventory_tokens.push(quote! {
                            ::service_kit::inventory::submit! {
                                ::service_kit::ApiDtoMetadata {
                                    name: #type_name_s,
                                    schema_provider: || (
                                        #type_name_s.to_string(),
                                        <#inner_type as ::service_kit::utoipa::PartialSchema>::schema(),
                                    ),
                                }
                            }
                        });
                    }
                }
            } else if type_to_string(&pat_type.ty) == "axum :: http :: HeaderMap" || type_to_string(&pat_type.ty).ends_with("::HeaderMap") || type_to_string(&pat_type.ty) == "HeaderMap" {
                // HeaderMap 注入支持（不进入 OpenAPI 参数）
                _needs_header_map = true;
                if let Pat::Ident(pat_ident) = &*pat_type.pat {
                    let var_ident = &pat_ident.ident;
                    arg_prepare_tokens.push(quote! {
                        let #var_ident: axum::http::HeaderMap = headers.clone();
                    });
                    call_args_tokens.push(quote! { #var_ident });
                }
            } else if let Some(inner_type) = get_inner_type(&pat_type.ty, "State") {
                // State<T> 注入（不进入 OpenAPI 参数）。支持 `State(s): State<T>` 与 `s: State<T>` 两种形态。
                _needs_state = true;
                _state_type_tokens = Some(quote! { #inner_type });
                let state_var_ident = format_ident!("__state_arg_{}", call_args_tokens.len());
                arg_prepare_tokens.push(quote! {
                    let #state_var_ident: axum::extract::State<#inner_type> = {
                        let any_arc = state_any.ok_or_else(|| ::service_kit::error::Error::SpecError("missing router state".to_string()))?;
                        let typed = (&*any_arc).downcast_ref::<#inner_type>().ok_or_else(|| ::service_kit::error::Error::SpecError("invalid router state type".to_string()))?;
                        axum::extract::State::<#inner_type>(typed.clone())
                    };
                });
                call_args_tokens.push(quote! { #state_var_ident });
            } else if let Some(inner_type) = get_inner_type(&pat_type.ty, "Json") {
                // 禁止在请求体中直接使用 Json<Vec<T>>，要求使用具名 DTO 包裹
                if get_inner_type(inner_type, "Vec").is_some() {
                    panic!("Json<Vec<T>> in request body is not allowed. Wrap it in a DTO with a Vec<T> field.");
                }
                let type_name = type_to_string(inner_type);
                let type_name_s = sanitize_component_name(&type_name);
                request_body_token = quote! {
                    Some(&::service_kit::ApiRequestBody {
                        description: "", // TODO: Parse from attributes
                        required: true,
                        type_name: #type_name_s,
                    })
                };
                // runtime wrapper: deserialize whole params into body T
                 let inner_ty_tokens = quote! { #inner_type };
                 let json_ident = syn::Ident::new("__json_body", proc_macro2::Span::call_site());
                arg_prepare_tokens.push(quote! {
                    // Prefer decoding from raw body when content-type is application/json
                    let __body_json_source: serde_json::Value = match __content_type_opt.as_deref() {
                        Some(ct) if ct.contains("application/json") => {
                            if __body_bytes.is_empty() {
                                serde_json::Value::Null
                            } else {
                                match serde_json::from_slice::<serde_json::Value>(&__body_bytes) {
                                    Ok(v) => v,
                                    Err(e) => return Err(::service_kit::error::Error::SerdeJson(e)),
                                }
                            }
                        },
                        _ => serde_json::Value::Null,
                    };
                    let #json_ident: #inner_ty_tokens = if !__body_json_source.is_null() {
                        match serde_json::from_value(__body_json_source) {
                            Ok(v) => v,
                            Err(e) => return Err(::service_kit::error::Error::SerdeJson(e)),
                        }
                    } else {
                        // Fallback (no body): try to construct from merged params (query/path only)
                        match serde_json::from_value(params.clone()) {
                            Ok(v) => v,
                            Err(e) => return Err(::service_kit::error::Error::SerdeJson(e)),
                        }
                    };
                    let #json_ident = axum::Json::<#inner_ty_tokens>(#json_ident);
                });
                call_args_tokens.push(quote! { #json_ident });

                // auto-register DTO for Json<T>
                let type_name = type_to_string(inner_type);
                let type_name_s = sanitize_component_name(&type_name);
                dto_inventory_tokens.push(quote! {
                    ::service_kit::inventory::submit! {
                        ::service_kit::ApiDtoMetadata {
                            name: #type_name_s,
                            schema_provider: || (
                                #type_name_s.to_string(),
                                <#inner_type as ::service_kit::utoipa::PartialSchema>::schema(),
                            ),
                        }
                    }
                });

                // auto-register nested generic instances appearing within Json<T>
                let nested_types = collect_concrete_generic_types(inner_type);
                for (nested_name, nested_ty) in nested_types {
                    let nested_name_lit = syn::LitStr::new(&nested_name, proc_macro2::Span::call_site());
                    let nested_ty_tokens = quote! { #nested_ty };
                    dto_inventory_tokens.push(quote! {
                        ::service_kit::inventory::submit! {
                            ::service_kit::ApiDtoMetadata {
                                name: #nested_name_lit,
                                schema_provider: || (
                                    #nested_name_lit.to_string(),
                                    <#nested_ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                                ),
                            }
                        }
                    });
                }

                // 数组不再注册为独立组件；仅依赖元素类型 T 的 schema 注册（已在递归收集中覆盖）
            }
            // Support Form<T> similar to Json<T> for OpenAPI and runtime adapter
            else if let Some(inner_type) = get_inner_type(&pat_type.ty, "Form") {
                if get_inner_type(inner_type, "Vec").is_some() {
                    panic!("Form<Vec<T>> in request body is not allowed. Wrap it in a DTO with a Vec<T> field.");
                }
                let type_name = type_to_string(inner_type);
                let type_name_s = sanitize_component_name(&type_name);
                request_body_token = quote! {
                    Some(&::service_kit::ApiRequestBody {
                        description: "", // TODO
                        required: true,
                        type_name: #type_name_s,
                    })
                };
                let inner_ty_tokens = quote! { #inner_type };
                let form_ident = syn::Ident::new("__form_body", proc_macro2::Span::call_site());
                arg_prepare_tokens.push(quote! {
                    // Decode application/x-www-form-urlencoded into a map then into T; allow empty body to fallback
                    let #form_ident: #inner_ty_tokens = match __content_type_opt.as_deref() {
                        Some(ct) if ct.contains("application/x-www-form-urlencoded") => {
                            let raw = String::from_utf8(__body_bytes.clone()).unwrap_or_default();
                            match ::service_kit::serde_urlencoded::from_str::<#inner_ty_tokens>(&raw) {
                                Ok(v) => v,
                                Err(_e) => {
                                    // As a fallback, attempt JSON decode for tolerant clients
                                    match serde_json::from_slice::<#inner_ty_tokens>(&__body_bytes) {
                                        Ok(v) => v,
                                        Err(e2) => return Err(::service_kit::error::Error::SerdeJson(e2)),
                                    }
                                }
                            }
                        },
                        _ => {
                            // No form content-type; try to build from merged params (query/path)
                            match serde_json::from_value(params.clone()) {
                                Ok(v) => v,
                                Err(e) => return Err(::service_kit::error::Error::SerdeJson(e)),
                            }
                        }
                    };
                    let #form_ident = axum::Form::<#inner_ty_tokens>(#form_ident);
                });
                call_args_tokens.push(quote! { #form_ident });
                // auto-register DTO for Form<T>
                let type_name = type_to_string(inner_type);
                let type_name_s = sanitize_component_name(&type_name);
                dto_inventory_tokens.push(quote! {
                    ::service_kit::inventory::submit! {
                        ::service_kit::ApiDtoMetadata {
                            name: #type_name_s,
                            schema_provider: || (
                                #type_name_s.to_string(),
                                <#inner_type as ::service_kit::utoipa::PartialSchema>::schema(),
                            ),
                        }
                    }
                });
                let nested_types = collect_concrete_generic_types(inner_type);
                for (nested_name, nested_ty) in nested_types {
                    let nested_name_lit = syn::LitStr::new(&nested_name, proc_macro2::Span::call_site());
                    let nested_ty_tokens = quote! { #nested_ty };
                    dto_inventory_tokens.push(quote! {
                        ::service_kit::inventory::submit! {
                            ::service_kit::ApiDtoMetadata {
                                name: #nested_name_lit,
                                schema_provider: || (
                                    #nested_name_lit.to_string(),
                                    <#nested_ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                                ),
                            }
                        }
                    });
                }
            }
        }
    }

    // --- Parse Responses ---
    let mut responses_tokens = Vec::new();
    let mut response_dto_inventory_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
    if let ReturnType::Type(_, ty) = &item_fn.sig.output {
        if let Some(inner_type) = get_inner_type(ty, "Json") {
            // 针对返回 Json<Vec<U>>：要求使用 output(list = "U")，并且不注册 Vec 组件
            if let Some(vec_inner) = get_inner_type(inner_type, "Vec") {
                if let Some((ref kind_ident, _)) = args_parsed.output {
                    if kind_ident.to_string().eq_ignore_ascii_case("list") {
                        // paths 由 output(list) 决定，这里不设置组件类型名
                        responses_tokens.push(quote! { ::service_kit::ApiResponse { status_code: 200, description: #summary, type_name: None } });
                        // 注册 U 及其允许的泛型具体化
                        let u_name = sanitize_component_name(&type_to_string(vec_inner));
                        let u_name_lit = syn::LitStr::new(&u_name, proc_macro2::Span::call_site());
                        let u_tokens = quote! { #vec_inner };
                        response_dto_inventory_tokens.push(quote! {
                            ::service_kit::inventory::submit! {
                                ::service_kit::ApiDtoMetadata {
                                    name: #u_name_lit,
                                    schema_provider: || (
                                        #u_name_lit.to_string(),
                                        <#u_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                                    ),
                                }
                            }
                        });
                        let nested_types = collect_concrete_generic_types(vec_inner);
                        for (nested_name, nested_ty) in nested_types {
                            let nested_name_lit = syn::LitStr::new(&nested_name, proc_macro2::Span::call_site());
                            let nested_ty_tokens = quote! { #nested_ty };
                            response_dto_inventory_tokens.push(quote! {
                                ::service_kit::inventory::submit! {
                                    ::service_kit::ApiDtoMetadata {
                                        name: #nested_name_lit,
                                        schema_provider: || (
                                            #nested_name_lit.to_string(),
                                            <#nested_ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                                        ),
                                    }
                                }
                            });
                        }
                    } else {
                        panic!("Json<Vec<T>> return type must use output(list = \"T\").");
                    }
                } else {
                    panic!("Json<Vec<T>> return type must use output(list = \"T\").");
                }
            } else {
                // 普通 Json<T> 响应：注册 T 组件
                let type_name = type_to_string(inner_type);
                let type_name_s = sanitize_component_name(&type_name);
                let type_name_lit = syn::LitStr::new(&type_name_s, proc_macro2::Span::call_site());
                responses_tokens.push(quote! {
                    ::service_kit::ApiResponse {
                        status_code: 200,
                        description: #summary,
                        type_name: Some(#type_name_lit),
                    }
                });
                let type_ident_tokens = quote! { #inner_type };
                response_dto_inventory_tokens.push(quote! {
                    ::service_kit::inventory::submit! {
                        ::service_kit::ApiDtoMetadata {
                            name: #type_name_lit,
                            schema_provider: || (
                                #type_name_lit.to_string(),
                                <#type_ident_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                            ),
                        }
                    }
                });
                let nested_types = collect_concrete_generic_types(inner_type);
                for (nested_name, nested_ty) in nested_types {
                    let nested_name_lit = syn::LitStr::new(&nested_name, proc_macro2::Span::call_site());
                    let nested_ty_tokens = quote! { #nested_ty };
                    response_dto_inventory_tokens.push(quote! {
                        ::service_kit::inventory::submit! {
                            ::service_kit::ApiDtoMetadata {
                                name: #nested_name_lit,
                                schema_provider: || (
                                    #nested_name_lit.to_string(),
                                    <#nested_ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                                ),
                            }
                        }
                    });
                }
            }
        }
    }
    // Add a default response if none was parsed
    if responses_tokens.is_empty() {
        responses_tokens.push(quote! {
            ::service_kit::ApiResponse { status_code: 200, description: "Success", type_name: None }
        });
    }

    // Append extra responses from macro args
    for (code_lit, desc_lit, ty_opt) in &args_parsed.responses {
        if let Some(ty) = ty_opt {
            let ty_str = type_to_string(ty);
            let ty_str_s = sanitize_component_name(&ty_str);
            let ty_str_lit = syn::LitStr::new(&ty_str_s, proc_macro2::Span::call_site());
            responses_tokens.push(quote! {
                ::service_kit::ApiResponse {
                    status_code: #code_lit,
                    description: #desc_lit,
                    type_name: Some(#ty_str_lit),
                }
            });
            let ty_tokens = quote! { #ty };
            response_dto_inventory_tokens.push(quote! {
                ::service_kit::inventory::submit! {
                    ::service_kit::ApiDtoMetadata {
                        name: #ty_str_lit,
                        schema_provider: || (
                            #ty_str_lit.to_string(),
                            <#ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                        ),
                    }
                }
            });

            // Recursively register nested concrete generic instances
            let nested_types = collect_concrete_generic_types(ty);
            for (nested_name, nested_ty) in nested_types {
                let nested_name_lit = syn::LitStr::new(&nested_name, proc_macro2::Span::call_site());
                let nested_ty_tokens = quote! { #nested_ty };
                response_dto_inventory_tokens.push(quote! {
                    ::service_kit::inventory::submit! {
                        ::service_kit::ApiDtoMetadata {
                            name: #nested_name_lit,
                            schema_provider: || (
                                #nested_name_lit.to_string(),
                                <#nested_ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                            ),
                        }
                    }
                });
            }
        } else {
            responses_tokens.push(quote! {
                ::service_kit::ApiResponse {
                    status_code: #code_lit,
                    description: #desc_lit,
                    type_name: None,
                }
            });
        }
    }

    // --- Generate Static Metadata ---
    let params_ident = format_ident!("__API_PARAMS_{}", fn_name_str.to_uppercase());
    let responses_ident = format_ident!("__API_RESPONSES_{}", fn_name_str.to_uppercase());
    let request_body_ident = format_ident!("__API_REQ_BODY_{}", fn_name_str.to_uppercase());

    let exec_fn_ident = format_ident!("__API_EXEC_{}", fn_name_str.to_uppercase());

    // Build output shape if provided
    let output_shape_token = if let Some((ref kind_ident, ref ty_lit)) = args_parsed.output {
        let kind = kind_ident.to_string();
        if kind.eq_ignore_ascii_case("detail") {
            quote! { Some(::service_kit::ApiOutputShape::Detail { type_name: #ty_lit }) }
        } else if kind.eq_ignore_ascii_case("list") {
            quote! { Some(::service_kit::ApiOutputShape::List { type_name: #ty_lit }) }
        } else {
            quote! { None }
        }
    } else { quote! { None } };

    let static_metadata = quote! {
        #[allow(non_upper_case_globals)]
        const #params_ident: &[::service_kit::ApiParameter] = &[#(#params_tokens),*];
        #[allow(non_upper_case_globals)]
        const #responses_ident: &[::service_kit::ApiResponse] = &[#(#responses_tokens),*];
        #[allow(non_upper_case_globals)]
        const #request_body_ident: Option<&'static ::service_kit::ApiRequestBody> = #request_body_token;

        ::service_kit::inventory::submit! {
            ::service_kit::ApiMetadata {
                operation_id: #fn_name_str,
                method: #method_str,
                path: #path_str,
                summary: #summary,
                description: #description,
                parameters: #params_ident,
                request_body: #request_body_ident,
                responses: #responses_ident,
                output: #output_shape_token,
            }
        }

        // Static handler function for REST/MCP routers
        fn #exec_fn_ident(__params_ref: &serde_json::Value, headers: axum::http::HeaderMap, __raw_query_str: Option<String>, state_any: Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>, __body_bytes: Vec<u8>, __content_type_opt: Option<String>) -> ::service_kit::handler::DynHandlerFuture {
            let __params_json = __params_ref.clone();
            Box::pin(async move {
                let params = __params_json.clone();
                #(#arg_prepare_tokens)*
                let __resp = #fn_ident(#(#call_args_tokens),*).await;
                let __resp = ::axum::response::IntoResponse::into_response(__resp);
                Ok(__resp)
            })
        }

        // Register executable handler
        ::service_kit::inventory::submit! {
            ::service_kit::handler::ApiHandlerInventory {
                operation_id: #fn_name_str,
                handler: #exec_fn_ident,
            }
        }
    };

    // --- Final Output ---
    let output = quote! {
        #static_metadata
        #(#dto_inventory_tokens)*
        #(#response_dto_inventory_tokens)*
        #item_fn
    };

    output.into()
}

fn type_to_string(ty: &Type) -> String {
    quote!(#ty).to_string().replace(' ', "")
}

// --- Component name sanitization for generic types ---
// Convert type strings like `ApiResponse<PaginatedResponse<ApiDefinition<FlowDefinitionData>>>`
// into a stable, generator-friendly name: `ApiResponseOfPaginatedResponseOfApiDefinitionOfFlowDefinitionData`.
fn sanitize_component_name(type_str: &str) -> String {
    // Fast path: no generics
    if !type_str.contains('<') {
        return strip_modules(type_str).to_string();
    }

    // Recursive descent parser over the string
    fn parse_type(input: &str, i: &mut usize) -> String {
        // parse identifier (may include module paths ::)
        let ident = parse_ident(input, i);
        let mut name = strip_modules(&ident).to_string();
        skip_ws(input, i);
        if *i < input.len() && input.as_bytes()[*i] as char == '<' {
            *i += 1; // consume '<'
            let mut args: Vec<String> = Vec::new();
            loop {
                skip_ws(input, i);
                let arg = parse_type(input, i);
                args.push(arg);
                skip_ws(input, i);
                if *i >= input.len() { break; }
                let ch = input.as_bytes()[*i] as char;
                if ch == ',' { *i += 1; continue; }
                if ch == '>' { *i += 1; break; }
                // unexpected character; advance to break potential loop
                *i += 1;
            }
            // Join generic args with underscores to match utoipa naming (Base_Arg1_Arg2)
            for (idx, arg) in args.into_iter().enumerate() {
                if idx == 0 { name.push('_'); } else { name.push('_'); }
                name.push_str(&arg);
            }
        }
        name
    }

    fn parse_ident(input: &str, i: &mut usize) -> String {
        let start = *i;
        while *i < input.len() {
            let ch = input.as_bytes()[*i] as char;
            match ch {
                '<' | '>' | ',' => break,
                _ => { *i += 1; }
            }
        }
        input[start..*i].to_string()
    }

    fn strip_modules(ident: &str) -> String {
        // remove module paths (keep the last segment after ::)
        let simple = ident.rsplit("::").next().unwrap_or(ident);
        // remove non-alphanumeric/underscore characters just in case
        simple.chars().filter(|c| c.is_ascii_alphanumeric() || *c == '_').collect()
    }

    fn skip_ws(_input: &str, _i: &mut usize) {}

    let mut idx = 0;
    let result = parse_type(type_str, &mut idx);
    // Fail-fast on clearly illegal names to avoid producing unusable component keys
    // Allowed: [A-Za-z0-9_], plus embedded "Of" sequences produced by our sanitizer
    if result.is_empty() || result.chars().any(|c| !(c.is_ascii_alphanumeric() || c == '_')) {
        panic!("Illegal sanitized component name generated: {} from {}", result, type_str);
    }
    result
}

// removed unused short_hash helper

fn get_inner_type<'a>(ty: &'a Type, type_name: &str) -> Option<&'a Type> {
    if let Type::Path(type_path) = ty {
        if let Some(segment) = type_path.path.segments.last() {
            if segment.ident == type_name {
                if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
                    if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
                        return Some(inner);
                    }
                }
            }
        }
    }
    None
}

// Collect all nested concrete generic type instantiations within a type. For example, for
// ApiResponse<PaginatedResponse<ApiDefinition<FlowDefinitionData>>> it returns pairs of
// (sanitized_name, Type) for:
// - ApiResponse<PaginatedResponse<ApiDefinition<FlowDefinitionData>>> (optional, caller already handles top)
// - PaginatedResponse<ApiDefinition<FlowDefinitionData>>
// - ApiDefinition<FlowDefinitionData>
fn collect_concrete_generic_types(ty: &Type) -> Vec<(String, Type)> {
    let mut acc: Vec<(String, Type)> = Vec::new();
    fn is_allowed_generic_base(ident: &syn::Ident) -> bool {
        let name = ident.to_string();
        matches!(name.as_str(), "ApiDefinition" | "PaginatedResponse" | "ApiResponse")
    }
    fn visit(t: &Type, out: &mut Vec<(String, Type)>) {
        match t {
            Type::Path(tp) => {
                if let Some(seg) = tp.path.segments.last() {
                    if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
                        // Only record allowed generic bases (avoid Option<Value>, Vec<...>, DateTime, etc.)
                        if is_allowed_generic_base(&seg.ident) {
                            let name = sanitize_component_name(&type_to_string(t));
                            out.push((name, t.clone()));
                        }
                        // Recurse into generic argument types
                        for arg in &args.args {
                            if let syn::GenericArgument::Type(inner_ty) = arg {
                                visit(inner_ty, out);
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    visit(ty, &mut acc);
    // Deduplicate by name to avoid duplicate inventory submissions
    let mut seen = std::collections::HashSet::new();
    acc.into_iter()
        .filter(|(name, _)| seen.insert(name.clone()))
        .collect()
}

// Helper: for Vec<U>, recursively collect allowed generic instances inside U
// 移除针对 Vec 的特殊辅助，数组一律内联

fn parse_doc_comments(attrs: &[Attribute]) -> (String, String) {
    let doc_comments: Vec<String> = attrs
        .iter()
        .filter_map(|attr| {
            if attr.path().is_ident("doc") {
                if let syn::Meta::NameValue(nv) = &attr.meta {
                    if let syn::Expr::Lit(expr_lit) = &nv.value {
                        if let syn::Lit::Str(lit) = &expr_lit.lit {
                            return Some(lit.value().trim().to_string());
                        }
                    }
                }
            }
            None
        })
        .collect();

    let description = doc_comments.join("\n");
    let summary = description.lines().next().unwrap_or("").to_string();
    (summary, description)
}

// --- `api_dto` and its helpers ---

#[derive(Debug, Default)]
struct ApiDtoArgs {
    // No arguments needed - we use Rust field names directly
}

impl syn::parse::Parse for ApiDtoArgs {
    fn parse(_input: ParseStream) -> Result<Self> {
        Ok(ApiDtoArgs::default())
    }
}

#[proc_macro_attribute]
pub fn api_dto(attr: TokenStream, item: TokenStream) -> TokenStream {
    let _args = parse_macro_input!(attr as ApiDtoArgs);
    let mut input = parse_macro_input!(item as syn::DeriveInput);
    
    let type_name = input.ident.clone();
    let type_name_str = type_name.to_string();

    // Use Rust field names directly - no rename_all needed
    let attributes_to_add = quote! {
        #[derive(
            Debug,
            Clone,
            serde::Serialize,
            serde::Deserialize,
            ::service_kit::utoipa::ToSchema
        )]
        #[serde(deny_unknown_fields)]
    };

    let mut parsed_attrs: Vec<syn::Attribute> =
        syn::parse::Parser::parse(syn::Attribute::parse_outer, attributes_to_add.into())
            .expect("Failed to parse attributes");
    // Prepend derives so helper attributes like #[serde(...)] appear after derives
    let mut new_attrs: Vec<syn::Attribute> = Vec::with_capacity(parsed_attrs.len() + input.attrs.len());
    new_attrs.append(&mut parsed_attrs);
    new_attrs.append(&mut input.attrs);
    input.attrs = new_attrs;

    if let syn::Data::Struct(ref mut data_struct) = input.data {
        if let syn::Fields::Named(ref mut fields) = data_struct.fields {
            for field in fields.named.iter_mut() {
                if let Type::Path(type_path) = &field.ty {
                    if is_recursive_type(&type_path.path, &input.ident.to_string()) {
                        field.attrs.push(syn::parse_quote! {
                            #[schema(value_type = Object)]
                        });
                    }
                }
            }
        }
    }

    // 针对字段中的泛型实例，收集并注册其具体实例（例如 ApiDefinition<AgentDefinitionData>）
    let mut nested_regs: Vec<proc_macro2::TokenStream> = Vec::new();
    if let syn::Data::Struct(ref data_struct) = input.data {
        if let syn::Fields::Named(ref fields_named) = data_struct.fields {
            for field in &fields_named.named {
                let nested = collect_concrete_generic_types(&field.ty);
                for (name, ty) in nested {
                    let name_lit = syn::LitStr::new(&name, proc_macro2::Span::call_site());
                    let ty_tokens = quote! { #ty };
                    nested_regs.push(quote! {
                        ::service_kit::inventory::submit! {
                            ::service_kit::ApiDtoMetadata {
                                name: #name_lit,
                                schema_provider: || (
                                    #name_lit.to_string(),
                                    <#ty_tokens as ::service_kit::utoipa::PartialSchema>::schema(),
                                ),
                            }
                        }
                    });
                }
            }
        }
    }

    // 注册 DTO schema 到 inventory（仅对非泛型类型注册；泛型类型需在具体实例处注册）
    let registration = if input.generics.params.is_empty() {
        quote! {
            ::service_kit::inventory::submit! {
                ::service_kit::ApiDtoMetadata {
                    name: #type_name_str,
                    schema_provider: || {
                        (
                            #type_name_str.to_string(),
                            <#type_name as ::service_kit::utoipa::PartialSchema>::schema(),
                        )
                    },
                }
            }
        }
    } else {
        quote! {}
    };

    let output = quote! {
        #input
        #registration
        #(#nested_regs)*
    };

    output.into()
}

fn is_recursive_type(path: &syn::Path, self_name: &str) -> bool {
    if let Some(segment) = path.segments.last() {
        let type_name = segment.ident.to_string();
        if type_name == "Box" || type_name == "Option" {
            if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
                if let Some(syn::GenericArgument::Type(Type::Path(inner_type_path))) = args.args.first()
                {
                    if type_name == "Option" {
                        if let Some(inner_segment) = inner_type_path.path.segments.last() {
                            if inner_segment.ident == "Box" {
                                return is_recursive_boxed_type(inner_segment, self_name);
                            }
                        }
                    } else {
                        return is_recursive_boxed_type(segment, self_name);
                    }
                }
            }
        }
    }
    false
}

fn is_recursive_boxed_type(segment: &syn::PathSegment, self_name: &str) -> bool {
    if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
        if let Some(syn::GenericArgument::Type(Type::Path(inner_type))) = args.args.first() {
            if let Some(inner_segment) = inner_type.path.segments.last() {
                return inner_segment.ident == self_name;
            }
        }
    }
    false
}