//! LLM推理组件 — proc macro实现
//!
//! Promptable: struct → markdown prompt文本（支持嵌套struct、Vec、基本类型）
//! Parseable: enum/struct → markdown输出解析

use proc_macro2::TokenStream;
use quote::{quote, format_ident};
use syn::{Data, DeriveInput, Fields, Lit, Meta};

// ============================================================
// Derive mode: Legacy aliases preserved for backward compat, both use Promptable/Parseable traits
// ============================================================

#[allow(dead_code)]
enum DeriveMode {
    Legacy,
    New,
}

/// Source of prompt text: literal string or file include.
#[derive(Clone)]
enum PromptSource {
    Literal(String),
    Include(String),
}

/// A prompt attribute: text source + optional sealed path references.
struct PromptAttr {
    source: PromptSource,
    sealed_refs: Vec<syn::Path>,
}

/// Extract prompt attributes from `#[prompt("text")]`, `#[prompt("text", path)]`,
/// or `#[prompt(include = "path")]` (New mode).
fn extract_prompt_attr(attrs: &[syn::Attribute]) -> Vec<PromptAttr> {
    let mut prompts = Vec::new();
    for attr in attrs {
        if attr.path().is_ident("prompt") {
            if let Ok(pa) = attr.parse_args_with(|input: syn::parse::ParseStream| {
                // Try parsing `include = "path"` first
                if input.peek(syn::Ident) {
                    let fork = input.fork();
                    if let Ok(ident) = fork.parse::<syn::Ident>() {
                        if ident == "include" && fork.peek(syn::Token![=]) {
                            // Commit to include syntax
                            input.parse::<syn::Ident>()?; // consume "include"
                            input.parse::<syn::Token![=]>()?;
                            let path_lit: syn::LitStr = input.parse()?;
                            // include + sealed_refs is forbidden
                            if input.peek(syn::Token![,]) {
                                return Err(input.error("include = \"path\" cannot be combined with sealed references"));
                            }
                            return Ok(PromptAttr { source: PromptSource::Include(path_lit.value()), sealed_refs: vec![] });
                        }
                    }
                }
                // Fall back to literal string syntax
                let lit: syn::LitStr = input.parse()?;
                let mut refs = Vec::new();
                while input.peek(syn::Token![,]) {
                    input.parse::<syn::Token![,]>()?;
                    refs.push(input.parse::<syn::Path>()?);
                }
                Ok(PromptAttr { source: PromptSource::Literal(lit.value()), sealed_refs: refs })
            }) {
                prompts.push(pa);
            }
        }
    }
    prompts
}

/// Extract discriminate path from `#[discriminate(path::to::SEALED)]` attribute (New mode).
fn extract_discriminate_attr(attrs: &[syn::Attribute]) -> Option<String> {
    for attr in attrs {
        if attr.path().is_ident("discriminate") {
            if let Ok(path) = attr.parse_args::<syn::Path>() {
                let segments: Vec<String> = path.segments.iter()
                    .map(|s| s.ident.to_string())
                    .collect();
                return Some(segments.join("::"));
            }
        }
    }
    None
}

/// Get field documentation based on derive mode.
/// Returns Vec<PromptAttr> — Legacy mode wraps doc comments with empty sealed_refs.
fn get_field_docs(attrs: &[syn::Attribute], mode: &DeriveMode) -> Vec<PromptAttr> {
    match mode {
        DeriveMode::Legacy => extract_doc_comments(attrs).into_iter()
            .map(|s| PromptAttr { source: PromptSource::Literal(s), sealed_refs: vec![] })
            .collect(),
        DeriveMode::New => extract_prompt_attr(attrs),
    }
}

/// Join multiple PromptAttrs into a single source + collected sealed refs.
/// All-Literal: joins strings. Single Include: returns Include. Mixed: compile error.
fn join_prompt_attrs(attrs: &[PromptAttr]) -> (PromptSource, Vec<syn::Path>) {
    let has_include = attrs.iter().any(|a| matches!(&a.source, PromptSource::Include(_)));
    if has_include {
        if attrs.len() != 1 {
            panic!("prompt(include = \"...\") cannot be mixed with other prompt attributes on the same field");
        }
        return (attrs[0].source.clone(), vec![]);
    }
    let text = attrs.iter().map(|a| match &a.source {
        PromptSource::Literal(s) => s.as_str(),
        _ => unreachable!(),
    }).collect::<Vec<_>>().join(" ");
    let refs: Vec<syn::Path> = attrs.iter().flat_map(|a| a.sealed_refs.clone()).collect();
    (PromptSource::Literal(text), refs)
}

/// Generate struct-level header TokenStream from PromptAttrs (comment lines).
fn gen_struct_header_tokens(attrs: &[PromptAttr]) -> TokenStream {
    if attrs.is_empty() {
        return quote! {};
    }
    let mut parts: Vec<TokenStream> = Vec::new();
    for attr in attrs {
        match &attr.source {
            PromptSource::Literal(s) => {
                parts.push(quote! {
                    __out.push_str(#s);
                    __out.push('\n');
                });
            }
            PromptSource::Include(path) => {
                parts.push(quote! {
                    {
                        let __inc = include_str!(#path);
                        for __line in __inc.lines() {
                            __out.push_str(__line);
                            __out.push('\n');
                        }
                    }
                });
            }
        }
    }
    quote! { #(#parts)* }
}

/// Generate a TokenStream expression that evaluates to a String title.
/// Literal without sealed refs: emits a string literal.
/// Literal with sealed refs: emits a format_sealed() call.
/// Include: emits include_str!() call.
fn gen_title_expr(source: &PromptSource, sealed_refs: &[syn::Path]) -> TokenStream {
    match source {
        PromptSource::Literal(template) => {
            if sealed_refs.is_empty() {
                quote!(#template)
            } else {
                quote!({
                    let __sealed = ::mad_hatter::sealed::format_sealed(#template, &[#(&#sealed_refs),*]);
                    __sealed.unseal().clone()
                })
            }
        }
        PromptSource::Include(path) => {
            quote!(include_str!(#path))
        }
    }
}

/// Get discriminate path based on derive mode.
fn get_field_discriminate(attrs: &[syn::Attribute], mode: &DeriveMode) -> Option<String> {
    match mode {
        DeriveMode::Legacy => get_markdown_discriminate(attrs),
        DeriveMode::New => extract_discriminate_attr(attrs),
    }
}

// ============================================================
// Type classification (shared by Promptable and Parseable)
// ============================================================

#[allow(clippy::large_enum_variant)]
enum FieldKind {
    String,
    Bool,
    Numeric,
    Vec(syn::Type),
    Option(Box<FieldKind>),
    Other,
}

fn classify_field_type(ty: &syn::Type) -> FieldKind {
    if let syn::Type::Path(type_path) = ty {
        if let Some(seg) = type_path.path.segments.last() {
            let name = seg.ident.to_string();
            match name.as_str() {
                "String" => return FieldKind::String,
                "bool" => return FieldKind::Bool,
                "u8" | "u16" | "u32" | "u64" | "usize" |
                "i8" | "i16" | "i32" | "i64" | "isize" |
                "f32" | "f64" => return FieldKind::Numeric,
                "Vec" => {
                    if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
                        if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
                            return FieldKind::Vec(inner.clone());
                        }
                    }
                }
                "Option" => {
                    if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
                        if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
                            let inner_kind = classify_field_type(inner);
                            return FieldKind::Option(Box::new(inner_kind));
                        }
                    }
                }
                _ => {}
            }
        }
    }
    FieldKind::Other
}


// ============================================================
// Promptable derive
// ============================================================

/// Collected info about a struct field for Promptable generation.
struct ToMarkdownField {
    ident: syn::Ident,
    kind: FieldKind,
    section_title: PromptSource,
    sealed_refs: Vec<syn::Path>,
    skip: bool,
    flatten: bool,
    weak: bool,
    date_format: Option<String>,
    volatile: bool,
}

#[allow(dead_code)]
/// Generate `impl Promptable for Struct` from derive attributes (Legacy entry point).
pub fn derive_to_markdown(input: DeriveInput) -> TokenStream {
    derive_to_markdown_inner(input, &DeriveMode::Legacy)
}

/// Internal: Generate rendering impl for Struct, parameterized by derive mode.
fn derive_to_markdown_inner(input: DeriveInput, mode: &DeriveMode) -> TokenStream {
    let struct_name = &input.ident;

    let fields = match &input.data {
        Data::Struct(data) => match &data.fields {
            Fields::Named(named) => &named.named,
            _ => {
                return syn::Error::new_spanned(struct_name, "Promptable only supports structs with named fields")
                    .to_compile_error();
            }
        },
        Data::Enum(_) => {
            return derive_to_markdown_enum_inner(input, mode);
        }
        _ => {
            return syn::Error::new_spanned(struct_name, "Promptable only supports structs or enums")
                .to_compile_error();
        }
    };

    // Check for struct-level #[prompt(format = "...")]
    if let Some(fmt_str) = extract_prompt_format(&input.attrs) {
        let (template, field_idents) = parse_format_string(&fmt_str);
        let self_fields: Vec<TokenStream> = field_idents.iter().map(|f| quote! { self.#f }).collect();
        let rendering_impl = quote! {
            fn to_markdown_depth(&self, __depth: usize) -> ::std::string::String {
                ::std::format!(#template, #(#self_fields),*)
            }
            fn to_markdown_item(&self) -> ::std::string::String {
                ::std::format!(#template, #(#self_fields),*)
            }
        };
        return match mode {
            DeriveMode::Legacy => quote! {
                impl ::mad_hatter::llm::Promptable for #struct_name {
                    #rendering_impl
                }
                impl ::mad_hatter::llm::StructInput for #struct_name {}
            },
            DeriveMode::New => quote! {
                impl ::mad_hatter::llm::Reflectable for #struct_name {}
                impl ::mad_hatter::llm::Promptable for #struct_name {
                    #rendering_impl
                }
            },
        };
    }

    // Extract struct-level docs based on derive mode
    let struct_docs = get_field_docs(&input.attrs, mode);
    let struct_header_tokens = gen_struct_header_tokens(&struct_docs);

    let mut tm_fields: Vec<ToMarkdownField> = Vec::new();
    for field in fields.iter() {
        let ident = field.ident.clone().unwrap();
        let skip = has_markdown_skip(&field.attrs) || has_derived_attr(&field.attrs);
        let flatten = has_markdown_flatten(&field.attrs);
        let docs = get_field_docs(&field.attrs, mode);
        let (section_title, sealed_refs) = if docs.is_empty() {
            (PromptSource::Literal(ident.to_string()), vec![])
        } else {
            join_prompt_attrs(&docs)
        };
        let kind = classify_field_type(&field.ty);
        let weak = has_markdown_weak(&field.attrs);
        let date_format = extract_date_format(&field.attrs);
        let volatile = has_prompt_volatile(&field.attrs);
        tm_fields.push(ToMarkdownField { ident, kind, section_title, sealed_refs, skip, flatten, weak, date_format, volatile });

    }

    // Name collision detection: case-insensitive check struct name vs field names
    {
        let struct_lower = struct_name.to_string().to_lowercase();
        for f in &tm_fields {
            if f.ident.to_string().to_lowercase() == struct_lower {
                return syn::Error::new_spanned(
                    &f.ident,
                    format!("field name '{}' collides with struct name '{}' (case-insensitive). This would cause ambiguous separators in markdown format.", f.ident, struct_name)
                ).to_compile_error();
            }
        }
    }

    let depth_renders: Vec<TokenStream> = tm_fields.iter().map(|f| {
        if f.skip { return quote! {}; }
        gen_depth_render(f)
    }).collect();

    let item_renders: Vec<TokenStream> = tm_fields.iter().map(|f| {
        if f.skip { return quote! {}; }
        gen_item_render(f)
    }).collect();

    let header_code = struct_header_tokens;

    // Volatile validation
    let volatile_count = tm_fields.iter().filter(|f| f.volatile).count();
    if volatile_count > 1 {
        return syn::Error::new_spanned(
            struct_name,
            "only one #[prompt(volatile)] field allowed per struct"
        ).to_compile_error();
    }
    let volatile_idx = tm_fields.iter().position(|f| f.volatile);
    if let Some(idx) = volatile_idx {
        if tm_fields[idx].skip {
            return syn::Error::new_spanned(
                &tm_fields[idx].ident,
                "#[prompt(volatile)] cannot be on a skipped field"
            ).to_compile_error();
        }
    }

    // Generate to_markdown_cached() override when volatile is present
    let cached_method = if let Some(idx) = volatile_idx {
        let stable_renders: Vec<TokenStream> = tm_fields[..idx].iter().map(|f| {
            if f.skip { return quote! {}; }
            gen_depth_render(f)
        }).collect();
        let delta_renders: Vec<TokenStream> = tm_fields[idx..].iter().map(|f| {
            if f.skip { return quote! {}; }
            gen_depth_render(f)
        }).collect();
        let header_for_cached = header_code.clone();

        quote! {
            fn to_markdown_cached(&self) -> ::mad_hatter::llm::CachedPrompt {
                let __depth: usize = 2;
                let mut __out = ::std::string::String::new();
                #header_for_cached
                #(#stable_renders)*
                let __stable = __out;
                let mut __out = ::std::string::String::new();
                #(#delta_renders)*
                ::mad_hatter::llm::CachedPrompt { stable: __stable, delta: __out }
            }
        }
    } else {
        quote! {}
    };

    let rendering_impl = quote! {
        fn to_markdown_depth(&self, __depth: usize) -> ::std::string::String {
            let mut __out = ::std::string::String::new();
            #header_code
            #(#depth_renders)*
            __out
        }
        fn to_markdown_item(&self) -> ::std::string::String {
            let mut __out = ::std::string::String::new();
            #(#item_renders)*
            __out
        }
        #cached_method
    };

    match mode {
        DeriveMode::Legacy => quote! {
            impl ::mad_hatter::llm::Promptable for #struct_name {
                #rendering_impl
            }
            impl ::mad_hatter::llm::StructInput for #struct_name {}
        },
        DeriveMode::New => quote! {
            impl ::mad_hatter::llm::Reflectable for #struct_name {}
            impl ::mad_hatter::llm::Promptable for #struct_name {
                #rendering_impl
            }
        },
    }
}

#[allow(dead_code)]
/// Generate `impl Promptable for Enum` from derive attributes (Legacy entry point).
fn derive_to_markdown_enum(input: DeriveInput) -> TokenStream {
    derive_to_markdown_enum_inner(input, &DeriveMode::Legacy)
}

/// Internal: Generate rendering impl for Enum, parameterized by derive mode.
fn derive_to_markdown_enum_inner(input: DeriveInput, mode: &DeriveMode) -> TokenStream {
    let enum_name = &input.ident;

    let variants_data = match &input.data {
        Data::Enum(data) => &data.variants,
        _ => unreachable!(),
    };

    // Name collision detection: enum name vs field names (case-insensitive)
    {
        let enum_lower = enum_name.to_string().to_lowercase();
        for v in variants_data {
            if let Fields::Named(named) = &v.fields {
                for f in &named.named {
                    let fname = f.ident.as_ref().unwrap().to_string();
                    if fname.to_lowercase() == enum_lower {
                        return syn::Error::new_spanned(
                            f.ident.as_ref().unwrap(),
                            format!("field name '{}' collides with enum name '{}' (case-insensitive). This would cause ambiguous separators in markdown format.", fname, enum_name)
                        ).to_compile_error();
                    }
                }
            }
        }
    }

    // Generate match arms for to_markdown_depth
    let depth_arms: Vec<TokenStream> = variants_data.iter().map(|v| {
        let variant_ident = &v.ident;
        let snake_name = to_snake_case(&variant_ident.to_string());

        match &v.fields {
            Fields::Unit => {
                quote! {
                    Self::#variant_ident => {
                        __out.push_str(#snake_name);
                        __out.push('\n');
                    }
                }
            }
            Fields::Named(named) => {
                let field_bindings: Vec<TokenStream> = named.named.iter()
                    .map(|f| {
                        let fident = f.ident.as_ref().unwrap();
                        if has_markdown_skip(&f.attrs) || has_derived_attr(&f.attrs) {
                            let skip_ident = syn::Ident::new(&format!("_{}", fident), fident.span());
                            quote! { #fident: #skip_ident }
                        } else {
                            quote! { #fident }
                        }
                    })
                    .collect();
                let field_renders: Vec<TokenStream> = named.named.iter().map(|f| {
                    let fident = f.ident.as_ref().unwrap();
                    let skip = has_markdown_skip(&f.attrs) || has_derived_attr(&f.attrs);
                    if skip { return quote! {}; }
                    let flatten = has_markdown_flatten(&f.attrs);
                    let docs = get_field_docs(&f.attrs, mode);
                    let (title, sealed_refs) = if docs.is_empty() {
                        (PromptSource::Literal(fident.to_string()), vec![])
                    } else {
                        join_prompt_attrs(&docs)
                    };
                    let title_expr = gen_title_expr(&title, &sealed_refs);
                    let kind = classify_field_type(&f.ty);
                    gen_enum_field_depth_render(fident, &title_expr, &kind, flatten)
                }).collect();
                quote! {
                    Self::#variant_ident { #(#field_bindings),* } => {
                        __out.push_str(#snake_name);
                        __out.push('\n');
                        #(#field_renders)*

                    }
                }
            }
            Fields::Unnamed(_) => {
                syn::Error::new_spanned(variant_ident, "Promptable does not support tuple variants")
                    .to_compile_error()
            }
        }
    }).collect();

    // Generate match arms for to_markdown_item
    let item_arms: Vec<TokenStream> = variants_data.iter().map(|v| {
        let variant_ident = &v.ident;
        let snake_name = to_snake_case(&variant_ident.to_string());

        match &v.fields {
            Fields::Unit => {
                quote! {
                    Self::#variant_ident => {
                        __out.push_str(#snake_name);
                        __out.push('\n');
                    }
                }
            }
            Fields::Named(named) => {
                let field_bindings: Vec<TokenStream> = named.named.iter()
                    .map(|f| {
                        let fident = f.ident.as_ref().unwrap();
                        if has_markdown_skip(&f.attrs) || has_derived_attr(&f.attrs) {
                            let skip_ident = syn::Ident::new(&format!("_{}", fident), fident.span());
                            quote! { #fident: #skip_ident }
                        } else {
                            quote! { #fident }
                        }
                    })
                    .collect();
                let field_renders: Vec<TokenStream> = named.named.iter().map(|f| {
                    let fident = f.ident.as_ref().unwrap();
                    let skip = has_markdown_skip(&f.attrs) || has_derived_attr(&f.attrs);
                    if skip { return quote! {}; }

                    let flatten = has_markdown_flatten(&f.attrs);
                    let field_name = fident.to_string();
                    let kind = classify_field_type(&f.ty);
                    gen_enum_field_item_render(fident, &field_name, &kind, flatten)
                }).collect();
                quote! {
                    Self::#variant_ident { #(#field_bindings),* } => {
                        __out.push_str(#snake_name);
                        __out.push('\n');
                        #(#field_renders)*
                    }
                }
            }
            Fields::Unnamed(_) => {
                syn::Error::new_spanned(variant_ident, "Promptable does not support tuple variants")
                    .to_compile_error()
            }
        }
    }).collect();

    let rendering_impl = quote! {
        fn to_markdown_depth(&self, __depth: usize) -> ::std::string::String {
            let mut __out = ::std::string::String::new();
            match self {
                #(#depth_arms)*
            }
            __out
        }
        fn to_markdown_item(&self) -> ::std::string::String {
            let mut __out = ::std::string::String::new();
            match self {
                #(#item_arms)*
            }
            __out
        }
    };

    match mode {
        DeriveMode::Legacy => quote! {
            impl ::mad_hatter::llm::Promptable for #enum_name {
                #rendering_impl
            }
            impl ::mad_hatter::llm::StructInput for #enum_name {}
        },
        DeriveMode::New => quote! {
            impl ::mad_hatter::llm::Reflectable for #enum_name {}
            impl ::mad_hatter::llm::Promptable for #enum_name {
                #rendering_impl
            }
        },
    }
}

/// Generate depth-mode rendering for a single enum variant field.
/// Similar to gen_depth_render but accesses field via local binding (not self.field).
fn gen_enum_field_depth_render(ident: &syn::Ident, title_expr: &TokenStream, kind: &FieldKind, flatten: bool) -> TokenStream {
    match kind {
        FieldKind::String => {
            quote! {
                {
                    let __val = #ident;
                    let __t = #title_expr;
                    if !__val.is_empty() {
                        if __val.contains('\n') {
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                            __out.push_str(__val);
                            __out.push('\n');
                        } else {
                            __out.push_str(&__t);
                            __out.push_str(": ");
                            __out.push_str(__val);
                            __out.push('\n');
                        }
                    }
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                {
                    let __t = #title_expr;
                    __out.push_str(&__t);
                    __out.push_str(": ");
                    __out.push_str(&#ident.to_string());
                    __out.push('\n');
                }
            }
        }
        FieldKind::Vec(inner_ty) => {
            let inner_kind = classify_field_type(inner_ty);
            let element_render = gen_vec_element_render_depth(&inner_kind, &format_ident!("__out"));
            quote! {
                {
                    let __items = #ident;
                    if !__items.is_empty() {
                        let __t = #title_expr;
                        let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                        __out.push_str(&::std::format!("\n{} {} {}\n\n", __hashes, __t, __hashes));
                        for (__i, __item) in __items.iter().enumerate() {
                            if __i > 0 { __out.push('\n'); }
                            #element_render
                        }
                    }
                }
            }
        }
        FieldKind::Option(inner_kind) => {
            gen_enum_option_depth_render(ident, title_expr, inner_kind, flatten)
        }
        FieldKind::Other => {
            if flatten {
                quote! {
                    {
                        __out.push_str(&#ident.to_markdown_depth(__depth));
                    }
                }
            } else {
                quote! {
                    {
                        let __nested = #ident.to_markdown_depth(__depth + 1);
                        let __t = #title_expr;
                        if __nested.contains('\n') {
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                            __out.push_str(&__nested);
                        } else if !__nested.is_empty() {
                            __out.push_str(&__t);
                            __out.push_str(": ");
                            __out.push_str(&__nested);
                            __out.push('\n');
                        }
                    }
                }
            }
        }
    }
}

/// Generate depth-mode rendering for Option<T> enum variant fields.
fn gen_enum_option_depth_render(ident: &syn::Ident, title_expr: &TokenStream, inner_kind: &FieldKind, flatten: bool) -> TokenStream {
    let value_render = match inner_kind {
        FieldKind::String => {
            quote! {
                let __t = #title_expr;
                if !__val.is_empty() {
                    if __val.contains('\n') {
                        let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                        __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                        __out.push_str(__val);
                        __out.push('\n');
                    } else {
                        __out.push_str(&__t);
                        __out.push_str(": ");
                        __out.push_str(__val);
                        __out.push('\n');
                    }
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                let __t = #title_expr;
                __out.push_str(&__t);
                __out.push_str(": ");
                __out.push_str(&__val.to_string());
                __out.push('\n');
            }
        }
        _ => {
            if flatten {
                quote! {
                    __out.push_str(&__val.to_markdown_depth(__depth));
                }
            } else {
                quote! {
                    let __nested = __val.to_markdown_depth(__depth + 1);
                    let __t = #title_expr;
                    if __nested.contains('\n') {
                        let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                        __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                        __out.push_str(&__nested);
                    } else if !__nested.is_empty() {
                        __out.push_str(&__t);
                        __out.push_str(": ");
                        __out.push_str(&__nested);
                        __out.push('\n');
                    }
                }
            }
        }
    };

    quote! {
        if let ::std::option::Option::Some(__val) = #ident {
            #value_render
        }
    }
}

/// Generate item-mode rendering for a single enum variant field.
fn gen_enum_field_item_render(ident: &syn::Ident, field_name: &str, kind: &FieldKind, flatten: bool) -> TokenStream {
    match kind {
        FieldKind::String => {
            quote! {
                {
                    let __val = #ident;
                    if !__val.is_empty() {
                        __out.push_str(#field_name);
                        __out.push_str(": ");
                        __out.push_str(__val);
                        __out.push('\n');
                    }
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                {
                    __out.push_str(#field_name);
                    __out.push_str(": ");
                    __out.push_str(&#ident.to_string());
                    __out.push('\n');
                }
            }
        }
        FieldKind::Vec(inner_ty) => {
            let inner_kind = classify_field_type(inner_ty);
            let element_render = gen_vec_element_render_item(&inner_kind);
            quote! {
                {
                    let __items = #ident;
                    if !__items.is_empty() {
                        __out.push_str(#field_name);
                        __out.push_str(":\n");
                        for __item in __items.iter() {
                            #element_render
                        }
                    }
                }
            }
        }
        FieldKind::Option(inner_kind) => {
            gen_enum_option_item_render(ident, field_name, inner_kind, flatten)
        }
        FieldKind::Other => {
            if flatten {
                quote! {
                    {
                        __out.push_str(&#ident.to_markdown_item());
                    }
                }
            } else {
                quote! {
                    {
                        __out.push_str(#field_name);
                        __out.push_str(": ");
                        __out.push_str(&#ident.to_markdown_item());
                        __out.push('\n');
                    }
                }
            }
        }
    }
}

/// Generate item-mode rendering for Option<T> enum variant fields.
fn gen_enum_option_item_render(ident: &syn::Ident, field_name: &str, inner_kind: &FieldKind, flatten: bool) -> TokenStream {
    let value_render = match inner_kind {
        FieldKind::String => {
            quote! {
                if !__val.is_empty() {
                    __out.push_str(#field_name);
                    __out.push_str(": ");
                    __out.push_str(__val);
                    __out.push('\n');
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                __out.push_str(#field_name);
                __out.push_str(": ");
                __out.push_str(&__val.to_string());
                __out.push('\n');
            }
        }
        _ => {
            if flatten {
                quote! {
                    __out.push_str(&__val.to_markdown_item());
                }
            } else {
                quote! {
                    __out.push_str(#field_name);
                    __out.push_str(": ");
                    __out.push_str(&__val.to_markdown_item());
                    __out.push('\n');
                }
            }
        }
    };

    quote! {
        if let ::std::option::Option::Some(__val) = #ident {
            #value_render
        }
    }
}

/// Generate rendering code for a field in to_markdown_depth mode (heading-based).
fn gen_depth_render(f: &ToMarkdownField) -> TokenStream {
    let ident = &f.ident;
    let title_expr = gen_title_expr(&f.section_title, &f.sealed_refs);

    // Date format early return: always inline, custom or default format
    if let Some(ref fmt) = f.date_format {
        return quote! {
            {
                let __t = #title_expr;
                __out.push_str(&__t);
                __out.push_str(": ");
                __out.push_str(&self.#ident.format(#fmt).to_string());
                __out.push('\n');
            }
        };
    }

    // Check if title contains $size variable (resolved after field rendering)
    let has_size_var = matches!(&f.section_title, PromptSource::Literal(s) if s.contains("$size"));

    match &f.kind {
        FieldKind::String => {
            if f.weak {
                quote! {
                    {
                        let __val = &self.#ident;
                        let __t = #title_expr;
                        if !__val.is_empty() {
                            if __val.contains('\n') {
                                __out.push_str(&::std::format!("\n// {}\n", __t));
                                __out.push_str(__val);
                                __out.push('\n');
                            } else {
                                __out.push_str(&__t);
                                __out.push_str(": ");
                                __out.push_str(__val);
                                __out.push('\n');
                            }
                        }
                    }
                }
            } else {
                quote! {
                    {
                        let __val = &self.#ident;
                        let __t = #title_expr;
                        if !__val.is_empty() {
                            if __val.contains('\n') {
                                let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                                __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                                __out.push_str(__val);
                                __out.push('\n');
                            } else {
                                __out.push_str(&__t);
                                __out.push_str(": ");
                                __out.push_str(__val);
                                __out.push('\n');
                            }
                        }
                    }
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                {
                    let __t = #title_expr;
                    __out.push_str(&__t);
                    __out.push_str(": ");
                    __out.push_str(&self.#ident.to_string());
                    __out.push('\n');
                }
            }
        }
        FieldKind::Vec(inner_ty) => {
            let inner_kind = classify_field_type(inner_ty);
            if has_size_var {
                // $size mode: render to temp buffer first, then replace $size with content length
                let content_buf = format_ident!("__content");
                let element_render = gen_vec_element_render_depth(&inner_kind, &content_buf);
                quote! {
                    {
                        let __items = &self.#ident;
                        if !__items.is_empty() {
                            let mut #content_buf = ::std::string::String::new();
                            for (__i, __item) in __items.iter().enumerate() {
                                if __i > 0 { #content_buf.push('\n'); }
                                #element_render
                            }
                            let __t = #title_expr;
                            let __t = __t.replace("$size", &#content_buf.len().to_string());
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n\n", __hashes, __t, __hashes));
                            __out.push_str(&#content_buf);
                        }
                    }
                }
            } else {
                let element_render = gen_vec_element_render_depth(&inner_kind, &format_ident!("__out"));
                quote! {
                    {
                        let __items = &self.#ident;
                        if !__items.is_empty() {
                            let __t = #title_expr;
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n\n", __hashes, __t, __hashes));
                            for (__i, __item) in __items.iter().enumerate() {
                                if __i > 0 { __out.push('\n'); }
                                #element_render
                            }
                        }
                    }
                }
            }
        }
        FieldKind::Option(inner_kind) => {
            gen_option_depth_render(ident, &title_expr, inner_kind, f.flatten, f.weak)
        }
        FieldKind::Other => {
            if f.flatten {
                quote! {
                    {
                        __out.push_str(&self.#ident.to_markdown_depth(__depth));
                    }
                }
            } else if has_size_var {
                // $size mode: render nested content first, then replace $size in title
                quote! {
                    {
                        let __nested = self.#ident.to_markdown_depth(__depth + 1);
                        let __t = #title_expr;
                        let __t = __t.replace("$size", &__nested.len().to_string());
                        if __nested.contains('\n') {
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                            __out.push_str(&__nested);
                        } else if !__nested.is_empty() {
                            __out.push_str(&__t);
                            __out.push_str(": ");
                            __out.push_str(&__nested);
                            __out.push('\n');
                        }
                    }
                }
            } else {
                quote! {
                    {
                        let __nested = self.#ident.to_markdown_depth(__depth + 1);
                        let __t = #title_expr;
                        if __nested.contains('\n') {
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                            __out.push_str(&__nested);
                        } else if !__nested.is_empty() {
                            __out.push_str(&__t);
                            __out.push_str(": ");
                            __out.push_str(&__nested);
                            __out.push('\n');
                        }
                    }
                }
            }
        }
    }
}

/// Generate code to render a single Vec element in depth mode.
/// `buf` is the identifier of the output buffer (e.g. `__out` or `__content`).
fn gen_vec_element_render_depth(inner_kind: &FieldKind, buf: &syn::Ident) -> TokenStream {
    match inner_kind {
        FieldKind::String => {
            quote! { #buf.push_str(__item); #buf.push('\n'); }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! { #buf.push_str(&__item.to_string()); #buf.push('\n'); }
        }
        FieldKind::Other => {
            // Struct elements: use depth mode to output struct-level headers
            quote! { #buf.push_str(&__item.to_markdown_depth(__depth + 1)); }
        }
        _ => {
            // Vec, Option: use compact item format
            quote! { #buf.push_str(&__item.to_markdown_item()); }
        }
    }
}

/// Generate depth-mode rendering for Option<T> fields.
fn gen_option_depth_render(ident: &syn::Ident, title_expr: &TokenStream, inner_kind: &FieldKind, flatten: bool, weak: bool) -> TokenStream {
    let value_render = match inner_kind {
        FieldKind::String => {
            if weak {
                quote! {
                    let __t = #title_expr;
                    if !__val.is_empty() {
                        if __val.contains('\n') {
                            __out.push_str(&::std::format!("\n// {}\n", __t));
                            __out.push_str(__val);
                            __out.push('\n');
                        } else {
                            __out.push_str(&__t);
                            __out.push_str(": ");
                            __out.push_str(__val);
                            __out.push('\n');
                        }
                    }
                }
            } else {
                quote! {
                    let __t = #title_expr;
                    if !__val.is_empty() {
                        if __val.contains('\n') {
                            let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                            __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                            __out.push_str(__val);
                            __out.push('\n');
                        } else {
                            __out.push_str(&__t);
                            __out.push_str(": ");
                            __out.push_str(__val);
                            __out.push('\n');
                        }
                    }
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                let __t = #title_expr;
                __out.push_str(&__t);
                __out.push_str(": ");
                __out.push_str(&__val.to_string());
                __out.push('\n');
            }
        }
        FieldKind::Vec(inner_ty) => {
            let inner_kind = classify_field_type(inner_ty);
            let element_render = gen_vec_element_render_depth(&inner_kind, &format_ident!("__out"));
            quote! {
                if !__val.is_empty() {
                    let __t = #title_expr;
                    let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                    __out.push_str(&::std::format!("\n{} {} {}\n\n", __hashes, __t, __hashes));
                    for (__i, __item) in __val.iter().enumerate() {
                        if __i > 0 { __out.push('\n'); }
                        #element_render
                    }
                }
            }
        }
        _ => {
            if flatten {
                quote! {
                    __out.push_str(&__val.to_markdown_depth(__depth));
                }
            } else {
                quote! {
                    let __nested = __val.to_markdown_depth(__depth + 1);
                    let __t = #title_expr;
                    if __nested.contains('\n') {
                        let __hashes: ::std::string::String = "#".repeat(__depth + 1);
                        __out.push_str(&::std::format!("\n{} {} {}\n", __hashes, __t, __hashes));
                        __out.push_str(&__nested);
                    } else if !__nested.is_empty() {
                        __out.push_str(&__t);
                        __out.push_str(": ");
                        __out.push_str(&__nested);
                        __out.push('\n');
                    }
                }
            }
        }
    };

    quote! {
        if let ::std::option::Option::Some(__val) = &self.#ident {
            #value_render
        }
    }
}

/// Generate rendering code for a field in to_markdown_item mode (compact "field: value" format).
fn gen_item_render(f: &ToMarkdownField) -> TokenStream {
    let ident = &f.ident;
    let field_name = ident.to_string();

    // Date format early return: always inline
    if let Some(ref fmt) = f.date_format {
        return quote! {
            {
                __out.push_str(#field_name);
                __out.push_str(": ");
                __out.push_str(&self.#ident.format(#fmt).to_string());
                __out.push('\n');
            }
        };
    }

    match &f.kind {
        FieldKind::String => {
            quote! {
                {
                    let __val = &self.#ident;
                    if !__val.is_empty() {
                        __out.push_str(#field_name);
                        __out.push_str(": ");
                        __out.push_str(__val);
                        __out.push('\n');
                    }
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                {
                    __out.push_str(#field_name);
                    __out.push_str(": ");
                    __out.push_str(&self.#ident.to_string());
                    __out.push('\n');
                }
            }
        }
        FieldKind::Vec(inner_ty) => {
            let inner_kind = classify_field_type(inner_ty);
            let element_render = gen_vec_element_render_item(&inner_kind);
            quote! {
                {
                    let __items = &self.#ident;
                    if !__items.is_empty() {
                        __out.push_str(#field_name);
                        __out.push_str(":\n");
                        for __item in __items.iter() {
                            #element_render
                        }
                    }
                }
            }
        }
        FieldKind::Option(inner_kind) => {
            gen_option_item_render(ident, &field_name, inner_kind, f.flatten)
        }
        FieldKind::Other => {
            if f.flatten {
                quote! {
                    {
                        __out.push_str(&self.#ident.to_markdown_item());
                    }
                }
            } else {
                quote! {
                    {
                        __out.push_str(#field_name);
                        __out.push_str(": ");
                        __out.push_str(&self.#ident.to_markdown_item());
                        __out.push('\n');
                    }
                }
            }
        }
    }
}

/// Generate code to render a single Vec element in item mode.
fn gen_vec_element_render_item(inner_kind: &FieldKind) -> TokenStream {
    match inner_kind {
        FieldKind::String => {
            quote! { __out.push_str(__item); __out.push('\n'); }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! { __out.push_str(&__item.to_string()); __out.push('\n'); }
        }
        _ => {
            quote! { __out.push_str(&__item.to_markdown_item()); __out.push('\n'); }
        }
    }
}

/// Generate item-mode rendering for Option<T> fields.
fn gen_option_item_render(ident: &syn::Ident, field_name: &str, inner_kind: &FieldKind, flatten: bool) -> TokenStream {
    let value_render = match inner_kind {
        FieldKind::String => {
            quote! {
                if !__val.is_empty() {
                    __out.push_str(#field_name);
                    __out.push_str(": ");
                    __out.push_str(__val);
                    __out.push('\n');
                }
            }
        }
        FieldKind::Bool | FieldKind::Numeric => {
            quote! {
                __out.push_str(#field_name);
                __out.push_str(": ");
                __out.push_str(&__val.to_string());
                __out.push('\n');
            }
        }
        FieldKind::Vec(inner_ty) => {
            let inner_kind = classify_field_type(inner_ty);
            let element_render = gen_vec_element_render_item(&inner_kind);
            quote! {
                if !__val.is_empty() {
                    __out.push_str(#field_name);
                    __out.push_str(":\n");
                    for __item in __val.iter() {
                        #element_render
                    }
                }
            }
        }
        _ => {
            if flatten {
                quote! {
                    __out.push_str(&__val.to_markdown_item());
                }
            } else {
                quote! {
                    __out.push_str(#field_name);
                    __out.push_str(": ");
                    __out.push_str(&__val.to_markdown_item());
                    __out.push('\n');
                }
            }
        }
    };

    quote! {
        if let ::std::option::Option::Some(__val) = &self.#ident {
            #value_render
        }
    }
}

// ============================================================
// Parseable derive
// ============================================================

/// Information about a single enum variant, extracted at macro time.
struct VariantInfo {
    ident: syn::Ident,
    snake_name: String,
    doc: PromptSource,
    doc_sealed_refs: Vec<syn::Path>,
    fields: Vec<FromFieldInfo>,
}

/// Information about a single field within a variant.
struct FromFieldInfo {
    _is_required: bool,
    name: String,
    doc: PromptSource,
    doc_sealed_refs: Vec<syn::Path>,
    is_option: bool,
    is_vec: bool,
    /// The inner type name for Option fields (e.g. "u64", "String")
    inner_type: String,
    /// The inner type name for Vec fields (e.g. "ReplaceBlock")
    vec_inner_type: String,
    /// The syn::Type for Vec inner type (for generating code)
    vec_inner_syn_type: Option<syn::Type>,
    /// Path to a Sealed constant for discriminate (e.g. "crate::bindings::db::SENDER_USER")
    discriminate_path: Option<String>,
    /// Regex patterns for post-processing extracted values
    regex_patterns: Vec<String>,
    /// Derived field info: this field is computed from another field, not parsed from LLM output
    derived_info: Option<DerivedInfo>,
}


#[allow(dead_code)]
/// Generate `impl Parseable for Enum` or `impl Parseable for Struct` (Legacy entry point).
pub fn derive_from_markdown(input: DeriveInput) -> TokenStream {
    derive_from_markdown_inner(input, &DeriveMode::Legacy)
}

/// Generate `impl Promptable for Struct/Enum` (New framework entry point).
pub fn derive_promptable(input: DeriveInput) -> TokenStream {
    derive_to_markdown_inner(input, &DeriveMode::New)
}

/// Generate `impl Parseable for Enum/Struct` (New framework entry point).
pub fn derive_parseable(input: DeriveInput) -> TokenStream {
    derive_from_markdown_inner(input, &DeriveMode::New)
}

/// Internal: Generate parsing impl, parameterized by derive mode.
fn derive_from_markdown_inner(input: DeriveInput, mode: &DeriveMode) -> TokenStream {
    match &input.data {
        Data::Enum(_) => derive_from_markdown_enum_inner(input, mode),
        Data::Struct(_) => derive_from_markdown_struct_inner(input, mode),
        _ => {
            syn::Error::new_spanned(&input.ident, "Parseable only supports enums and structs")
                .to_compile_error()
        }
    }
}

// ============================================================
// Parseable for enum
// ============================================================

#[allow(dead_code)]
fn derive_from_markdown_enum(input: DeriveInput) -> TokenStream {
    derive_from_markdown_enum_inner(input, &DeriveMode::Legacy)
}

fn derive_from_markdown_enum_inner(input: DeriveInput, mode: &DeriveMode) -> TokenStream {
    let enum_name = &input.ident;
    let enum_name_str = enum_name.to_string();

    let variants_data = match &input.data {
        Data::Enum(data) => &data.variants,
        _ => unreachable!(),
    };

    let mut variants: Vec<VariantInfo> = Vec::new();
    for v in variants_data {
        let docs = get_field_docs(&v.attrs, mode);
        let (doc, doc_sealed_refs) = if docs.is_empty() {
            (PromptSource::Literal(String::new()), vec![])
        } else {
            join_prompt_attrs(&docs)
        };
        let snake_name = to_snake_case(&v.ident.to_string());

        let fields = match &v.fields {
            Fields::Named(named) => {
                named.named.iter().map(|f| {
                    let fname = f.ident.as_ref().unwrap().to_string();
                    let docs = get_field_docs(&f.attrs, mode);
                    let (fdoc, fdoc_sealed_refs) = if docs.is_empty() {
                        (PromptSource::Literal(fname.clone()), vec![])
                    } else {
                        join_prompt_attrs(&docs)
                    };
                    let (is_option, inner_type) = check_option_type(&f.ty);
                    let (is_vec, vec_inner_type, vec_inner_syn_type) = check_vec_type(&f.ty);
                    let is_required = has_markdown_required(&f.attrs);
                    let discriminate_path = get_field_discriminate(&f.attrs, mode);
                    let regex_patterns = extract_parse_regex(&f.attrs);
                    let derived_info = extract_derived_attr(&f.attrs);
                    FromFieldInfo { name: fname, doc: fdoc, doc_sealed_refs: fdoc_sealed_refs, is_option, is_vec, inner_type, vec_inner_type, vec_inner_syn_type, _is_required: is_required, discriminate_path, regex_patterns, derived_info }
                }).collect()

            }
            Fields::Unit => Vec::new(),
            _ => {
                return syn::Error::new_spanned(&v.ident, "Parseable only supports unit or named-field variants")
                    .to_compile_error();
            }
        };

        variants.push(VariantInfo {
            ident: v.ident.clone(),
            snake_name,
            doc,
            doc_sealed_refs,
            fields,
        });
    }

    // Name collision detection (case-insensitive)
    {
        let type_lower = enum_name_str.to_lowercase();
        for vi in &variants {
            for f in &vi.fields {
                if f.name.to_lowercase() == type_lower {
                    return syn::Error::new_spanned(
                        enum_name,
                        format!("Parseable: field name '{}' collides with type name '{}' (case-insensitive). This would cause ambiguous separators.", f.name, enum_name_str)
                    ).to_compile_error();
                }
            }
        }
    }

    let schema_body = gen_schema_markdown(&enum_name_str, &variants);
    let parse_body = gen_from_markdown(enum_name, &enum_name_str, &variants);

    match mode {
        DeriveMode::Legacy => quote! {
            impl ::mad_hatter::llm::Parseable for #enum_name {
                fn schema_markdown(token: &str) -> ::std::string::String {
                    #schema_body
                }
                fn from_markdown(text: &str, token: &str) -> ::std::result::Result<::std::vec::Vec<Self>, ::std::string::String> {
                    #parse_body
                }
                fn type_name() -> &'static str {
                    #enum_name_str
                }
            }
            impl ::mad_hatter::llm::StructOutput for #enum_name {}
        },
        DeriveMode::New => quote! {
            impl ::mad_hatter::llm::Reflectable for #enum_name {}
            impl ::mad_hatter::llm::Promptable for #enum_name {
                fn to_markdown_depth(&self, _depth: usize) -> ::std::string::String {
                    ::std::string::String::new()
                }
                fn schema_markdown(token: &str) -> ::std::string::String {
                    #schema_body
                }
            }
            impl ::mad_hatter::llm::Parseable for #enum_name {
                fn from_markdown(text: &str, token: &str) -> ::std::result::Result<::std::vec::Vec<Self>, ::std::string::String> {
                    #parse_body
                }
                fn type_name() -> &'static str {
                    #enum_name_str
                }
            }
        },
    }
}

/// Generate the body of `schema_markdown` for enum.
fn gen_schema_markdown(enum_name_str: &str, variants: &[VariantInfo]) -> TokenStream {
    let mut variant_schemas = Vec::new();

    for vi in variants {
        let snake = &vi.snake_name;
        let doc_expr = gen_title_expr(&vi.doc, &vi.doc_sealed_refs);
        let parsed_fields_for_schema: Vec<&FromFieldInfo> = vi.fields.iter().filter(|f| f.derived_info.is_none()).collect();
        let _required_fields: Vec<&FromFieldInfo> = parsed_fields_for_schema.iter().filter(|f| !f.is_option).copied().collect();
        let _option_fields: Vec<&FromFieldInfo> = parsed_fields_for_schema.iter().filter(|f| f.is_option).copied().collect();
        let total = parsed_fields_for_schema.len();


        if total == 0 {
            variant_schemas.push(quote! {
                {
                    let __d = #doc_expr;
                    __out.push_str(&::std::format!(
                        "// {}\n{ename}-{token}\n{snake}\n",
                        __d, ename = #enum_name_str, token = token, snake = #snake
                    ));
                }
            });
        } else {
            let mut field_lines = Vec::new();
            for f in &parsed_fields_for_schema {
                let fname = &f.name;

                let fdoc_expr = gen_title_expr(&f.doc, &f.doc_sealed_refs);
                if f.is_vec {
                    let vec_type = &f.vec_inner_type;
                    if vec_type == "String" {
                        field_lines.push(quote! {
                            {
                                let __fd = #fdoc_expr;
                                __out.push_str(&::std::format!(
                                    "{fname}-{token}\n// {}（每行一个字符串值）\n",
                                    __fd, fname = #fname, token = token
                                ));
                            }
                        });
                    } else {
                        field_lines.push(quote! {
                            {
                                let __fd = #fdoc_expr;
                                __out.push_str(&::std::format!(
                                    "{fname}-{token}\n// {}（多个{vtype}元素，用{vtype}-{token}分隔）\n",
                                    __fd, fname = #fname, token = token, vtype = #vec_type
                                ));
                            }
                        });
                    }
                } else if f.is_option {
                    field_lines.push(quote! {
                        {
                            let __fd = #fdoc_expr;
                            __out.push_str(&::std::format!(
                                "{fname}-{token}\n// {}（可选）\n",
                                __fd, fname = #fname, token = token
                            ));
                        }
                    });
                } else {
                    field_lines.push(quote! {
                        {
                            let __fd = #fdoc_expr;
                            __out.push_str(&::std::format!(
                                "{fname}-{token}\n// {}\n",
                                __fd, fname = #fname, token = token
                            ));
                        }
                    });
                }
            }
            variant_schemas.push(quote! {
                {
                    let __d = #doc_expr;
                    __out.push_str(&::std::format!(
                        "// {}\n{ename}-{token}\n{snake}\n",
                        __d, ename = #enum_name_str, token = token, snake = #snake
                    ));
                }
                #(#field_lines)*
            });
        }

        variant_schemas.push(quote! {
            __out.push('\n');
        });
    }

    quote! {
        let mut __out = ::std::string::String::new();
        #(#variant_schemas)*
        __out
    }
}

/// Generate the body of `from_markdown` for enum.
fn gen_from_markdown(enum_name: &syn::Ident, enum_name_str: &str, variants: &[VariantInfo]) -> TokenStream {
    let mut match_arms = Vec::new();
    let variant_list_str = variants.iter().map(|v| v.snake_name.as_str()).collect::<Vec<&str>>().join(", ");

    for vi in variants {
        let snake = &vi.snake_name;
        let variant_ident = &vi.ident;
        let field_count = vi.fields.len();

        if field_count == 0 {
            match_arms.push(quote! {
                #snake => {
                    __results.push(#enum_name::#variant_ident);
                }
            });
        } else {
            let multi_parse = gen_multi_field_parse(enum_name, enum_name_str, vi);
            match_arms.push(quote! {
                #snake => {
                    #multi_parse
                }
            });
        }
    }

    quote! {
        let __stripped = ::mad_hatter::llm::strip_code_block(text);
        let text = __stripped.as_str();

        let mut __results: ::std::vec::Vec<#enum_name> = ::std::vec::Vec::new();
        let __element_sep = ::std::format!("{}-{}", #enum_name_str, token);

        let __text_trimmed = text.trim();
        let __text_body = __text_trimmed;

        // Split by element separator
        let __sep_with_newline = ::std::format!("{}\n", __element_sep);
        let __chunks: ::std::vec::Vec<&str> = __text_body.split(&__sep_with_newline).collect();

        // Expect: first chunk (before first separator) must be empty
        if let ::std::option::Option::Some(__first_chunk) = __chunks.first() {
            let __first_trimmed = __first_chunk.trim();
            if !__first_trimmed.is_empty() {
                return ::std::result::Result::Err(
                    ::std::format!("[{}] FORMAT VIOLATION: Output REJECTED. You MUST start with the separator `{}`, not garbage text. You wrote: `{}`. NO preamble, NO thinking, NO commentary before the separator. Your output gets thrown away every single time you do this.", #enum_name_str, __element_sep, __first_trimmed)
                );
            }
        }

        let mut __parsed_count: usize = 0;

        for __chunk in __chunks {
            let __chunk = __chunk.trim();
            if __chunk.is_empty() { continue; }

            // First line is variant name
            let (__variant_line, __body) = match __chunk.find('\n') {
                Some(pos) => (__chunk[..pos].trim(), &__chunk[pos + 1..]),
                None => (__chunk.trim(), ""),
            };

            match __variant_line {
                #(#match_arms)*
                _ => {
                    return ::std::result::Result::Err(
                        ::std::format!("[{}] Unknown variant.\n  Found: '{}'\n  Expected one of: {}\n  ({} element(s) parsed successfully before this error)", #enum_name_str, __variant_line, #variant_list_str, __parsed_count)
                    );
                }
            }

            __parsed_count += 1;
        }

        ::std::result::Result::Ok(__results)
    }
}

/// Generate parsing code for a variant with N>=2 fields.
fn gen_multi_field_parse(enum_name: &syn::Ident, enum_name_str: &str, vi: &VariantInfo) -> TokenStream {
    let variant_ident = &vi.ident;
    let snake = &vi.snake_name;

    // Separate parsed fields (from LLM output) and derived fields (computed post-parse)
    let parsed_fields: Vec<&FromFieldInfo> = vi.fields.iter().filter(|f| f.derived_info.is_none()).collect();
    let derived_fields: Vec<&FromFieldInfo> = vi.fields.iter().filter(|f| f.derived_info.is_some()).collect();

    let mut field_names: Vec<String> = Vec::new();
    let mut field_idents: Vec<syn::Ident> = Vec::new();

    for f in &parsed_fields {
        field_names.push(f.name.clone());
        field_idents.push(syn::Ident::new(&f.name, proc_macro2::Span::call_site()));
    }

    let field_count = parsed_fields.len();
    let sep_name_literals: Vec<&str> = field_names.iter().map(|s| s.as_str()).collect();
    let field_optional_tokens: Vec<TokenStream> = parsed_fields.iter().map(|f| {
        if f.is_option { quote!(true) } else { quote!(false) }
    }).collect();

    let mut field_extractions = Vec::new();
    let mut field_var_names: Vec<syn::Ident> = Vec::new();

    for (pi, f) in parsed_fields.iter().enumerate() {
        let i = pi; // parsed index for field_values access
        let var_name = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
        let fname = &f.name;

        if f.is_vec {
            if f.vec_inner_type == "String" {
                // Vec<String> field: parse each non-empty, non-comment line as a string
                field_extractions.push(quote! {
                    let #var_name: ::std::vec::Vec<::std::string::String> = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        __raw.lines()
                            .map(|line| line.trim())
                            .filter(|line| !line.is_empty() && !line.starts_with("//"))
                            .map(|line| line.to_string())
                            .collect()
                    };
                });
            } else {
                // Vec<T> field: call T::from_markdown on the extracted text
                let vec_inner_ty = f.vec_inner_syn_type.as_ref().unwrap();
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        <#vec_inner_ty as ::mad_hatter::llm::Parseable>::from_markdown(__raw, token)?
                    };
                });
            }
        } else if f.is_option {
            let inner = &f.inner_type;
            if is_parseable_primitive(inner) {
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| s.trim()).unwrap_or("");
                        if __raw.is_empty() || __raw.starts_with("//") {
                            ::std::option::Option::None
                        } else {
                            match __raw.parse() {
                                Ok(v) => ::std::option::Option::Some(v),
                                Err(e) => return ::std::result::Result::Err(
                                    ::std::format!("[{}] Failed to parse field '{}' of variant '{}': {}", #enum_name_str, #fname, #snake, e)
                                ),
                            }
                        }
                    };
                });
            } else {
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| s.trim_end()).unwrap_or("");
                        if __raw.is_empty() || __raw.trim_start().starts_with("//") {
                            ::std::option::Option::None
                        } else {
                            ::std::option::Option::Some(__raw.to_string())
                        }
                    };
                });
            }
        } else {
            let fname_str = &f.name;
            if let Some(ref disc_path) = f.discriminate_path {
                let path_expr: syn::ExprPath = syn::parse_str(disc_path).expect("invalid discriminate path");
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        let __val = if #i == #field_count - 1 {
                            __raw.trim_end().to_string()
                        } else {
                            __raw.trim_end_matches('\n').to_string()
                        };
                        #path_expr.discriminate_owned(__val)
                    };
                });
            } else if is_parseable_primitive(&f.inner_type) {
                // 非Option数字字段：必填，parse成数字
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| s.trim()).unwrap_or("");
                        if __raw.is_empty() || __raw.starts_with("//") {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Required field '{}' of variant '{}' is empty", #enum_name_str, #fname_str, #snake)
                            );
                        }
                        match __raw.parse() {
                            Ok(v) => v,
                            Err(e) => return ::std::result::Result::Err(
                                ::std::format!("[{}] Failed to parse field '{}' of variant '{}': {}", #enum_name_str, #fname_str, #snake, e)
                            ),
                        }
                    };
                });
            } else {
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        let __val = if #i == #field_count - 1 {
                            __raw.trim_end().to_string()
                        } else {
                            __raw.trim_end_matches('\n').to_string()
                        };
                        if __val.trim().is_empty() {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Required field '{}' of variant '{}' is empty", #enum_name_str, #fname_str, #snake)
                            );
                        }
                        __val
                    };
                });
            }
        }

        field_var_names.push(var_name);
    }

    // Regex post-processing for String fields with #[parse(regex = [...])]
    for f in parsed_fields.iter() {
        if !f.regex_patterns.is_empty() && !f.is_vec {
            let var_name = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
            let patterns: Vec<&str> = f.regex_patterns.iter().map(|s| s.as_str()).collect();
            if f.is_option && f.inner_type == "String" {
                field_extractions.push(quote! {
                    let #var_name = #var_name.map(|__v| ::mad_hatter::llm::apply_regex_extraction(&__v, &[#(#patterns),*]));
                });
            } else if !f.is_option {
                field_extractions.push(quote! {
                    let #var_name = ::mad_hatter::llm::apply_regex_extraction(&#var_name, &[#(#patterns),*]);
                });
            }
        }
    }

    // Derived field computation (after all parsed fields including regex post-processing)
    for f in &derived_fields {
        let derived = f.derived_info.as_ref().unwrap();
        let var_name = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
        let source_var = syn::Ident::new(&format!("__field_{}", derived.from_field), proc_macro2::Span::call_site());
        let comparison = match &derived.matches_value {
            DerivedMatchValue::ConstPath(path) => quote! { #source_var == *#path.unseal() },
            DerivedMatchValue::Literal(lit) => quote! { #source_var == #lit },
        };
        field_extractions.push(quote! {
            let #var_name: bool = #comparison;
        });
        field_var_names.push(var_name);
    }

    // Constructor includes all fields (parsed + derived), using __field_{name} variable convention
    let constructor_fields: Vec<TokenStream> = vi.fields.iter().map(|f| {
        let fi = syn::Ident::new(&f.name, proc_macro2::Span::call_site());
        let vn = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
        quote! { #fi: #vn }
    }).collect();




    quote! {
        let __seps: ::std::vec::Vec<::std::string::String> = [#(#sep_name_literals),*]
            .iter()
            .map(|n| ::std::format!("{}-{}", n, token))
            .collect();

        let __field_names: &[&str] = &[#(#sep_name_literals),*];
        let __is_optional: &[bool] = &[#(#field_optional_tokens),*];

        // Phase 1: Find all separator positions
        let mut __found: ::std::vec::Vec<(usize, usize, usize)> = ::std::vec::Vec::new();
        for (__fi, __sep) in __seps.iter().enumerate() {
            let __sep_nl = ::std::format!("{}\n", __sep);
            if let Some(__pos) = __body.find(&__sep_nl) {
                __found.push((__fi, __pos, __pos + __sep_nl.len()));
            } else {
                let __trimmed = __body.trim_end();
                if __trimmed.ends_with(__sep.as_str()) {
                    let __sp = __trimmed.len() - __sep.len();
                    __found.push((__fi, __sp, __body.len()));
                }
            }
        }

        // Phase 2: Sort by position, verify order + required
        __found.sort_by_key(|&(_, __pos, _)| __pos);

        {
            let mut __prev_fi: ::std::option::Option<usize> = ::std::option::Option::None;
            for &(__fi, _, _) in &__found {
                if let ::std::option::Option::Some(__prev) = __prev_fi {
                    if __fi <= __prev {
                        return ::std::result::Result::Err(
                            ::std::format!("[{}::{}] Field order violation: '{}' must appear before '{}'",
                                #enum_name_str, #snake, __field_names[__fi], __field_names[__prev])
                        );
                    }
                    for __skipped in (__prev + 1)..__fi {
                        if !__is_optional[__skipped] {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}::{}] Required field '{}' is missing (expected between '{}' and '{}')",
                                    #enum_name_str, #snake, __field_names[__skipped], __field_names[__prev], __field_names[__fi])
                            );
                        }
                    }
                } else {
                    for __skipped in 0..__fi {
                        if !__is_optional[__skipped] {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}::{}] Required field '{}' is missing (expected before '{}')",
                                    #enum_name_str, #snake, __field_names[__skipped], __field_names[__fi])
                            );
                        }
                    }
                }
                __prev_fi = ::std::option::Option::Some(__fi);
            }
            let __last_fi = __prev_fi;
            if let ::std::option::Option::Some(__last) = __last_fi {
                for __skipped in (__last + 1)..__field_names.len() {
                    if !__is_optional[__skipped] {
                        return ::std::result::Result::Err(
                            ::std::format!("[{}::{}] Required field '{}' is missing (expected after '{}')",
                                #enum_name_str, #snake, __field_names[__skipped], __field_names[__last])
                        );
                    }
                }
            } else {
                for __skipped in 0..__field_names.len() {
                    if !__is_optional[__skipped] {
                        return ::std::result::Result::Err(
                            ::std::format!("[{}::{}] Required field '{}' is missing",
                                #enum_name_str, #snake, __field_names[__skipped])
                        );
                    }
                }
            }
        }

        // Phase 3: Extract content
        let mut __field_values: ::std::vec::Vec<&str> = ::std::vec::Vec::new();
        __field_values.resize(__seps.len(), "");
        for (__idx, &(__fi, _, __content_start)) in __found.iter().enumerate() {
            let __content_end = if __idx + 1 < __found.len() {
                __found[__idx + 1].1
            } else {
                __body.len()
            };
            __field_values[__fi] = &__body[__content_start..__content_end];
        }

        #(#field_extractions)*

        __results.push(#enum_name::#variant_ident {
            #(#constructor_fields),*
        });
    }
}

// ============================================================
// Parseable for struct
// ============================================================

#[allow(dead_code)]
fn derive_from_markdown_struct(input: DeriveInput) -> TokenStream {
    derive_from_markdown_struct_inner(input, &DeriveMode::Legacy)
}

fn derive_from_markdown_struct_inner(input: DeriveInput, mode: &DeriveMode) -> TokenStream {
    let struct_name = &input.ident;
    let struct_name_str = struct_name.to_string();

    let fields = match &input.data {
        Data::Struct(data) => match &data.fields {
            Fields::Named(named) => &named.named,
            _ => {
                return syn::Error::new_spanned(struct_name, "Parseable for struct only supports named fields")
                    .to_compile_error();
            }
        },
        _ => unreachable!(),
    };

    let mut field_infos: Vec<FromFieldInfo> = Vec::new();
    for f in fields.iter() {
        let fname = f.ident.as_ref().unwrap().to_string();
        let docs = get_field_docs(&f.attrs, mode);
        let (fdoc, fdoc_sealed_refs) = if docs.is_empty() {
            (PromptSource::Literal(fname.clone()), vec![])
        } else {
            join_prompt_attrs(&docs)
        };
        let (is_option, inner_type) = check_option_type(&f.ty);
        let (is_vec, vec_inner_type, vec_inner_syn_type) = check_vec_type(&f.ty);
        let is_required = has_markdown_required(&f.attrs);
        let discriminate_path = get_field_discriminate(&f.attrs, mode);
        let regex_patterns = extract_parse_regex(&f.attrs);
        let derived_info = extract_derived_attr(&f.attrs);
        field_infos.push(FromFieldInfo { name: fname, doc: fdoc, doc_sealed_refs: fdoc_sealed_refs, is_option, is_vec, inner_type, vec_inner_type, vec_inner_syn_type, _is_required: is_required, discriminate_path, regex_patterns, derived_info });
    }


    let _field_count = field_infos.len();

    // Generate schema_markdown for struct
    // Name collision detection (case-insensitive)
    {
        let type_lower = struct_name_str.to_lowercase();
        for f in &field_infos {
            if f.name.to_lowercase() == type_lower {
                return syn::Error::new_spanned(
                    struct_name,
                    format!("Parseable: field name '{}' collides with type name '{}' (case-insensitive). This would cause ambiguous separators.", f.name, struct_name_str)
                ).to_compile_error();
            }
        }
    }

    let schema_body = gen_struct_schema_markdown(&struct_name_str, &field_infos);

    // Generate from_markdown for struct
    let parse_body = gen_struct_from_markdown(struct_name, &struct_name_str, &field_infos);

    match mode {
        DeriveMode::Legacy => quote! {
            impl ::mad_hatter::llm::Parseable for #struct_name {
                fn schema_markdown(token: &str) -> ::std::string::String {
                    #schema_body
                }
                fn from_markdown(text: &str, token: &str) -> ::std::result::Result<::std::vec::Vec<Self>, ::std::string::String> {
                    #parse_body
                }
                fn type_name() -> &'static str {
                    #struct_name_str
                }
            }
            impl ::mad_hatter::llm::StructOutput for #struct_name {}
        },
        DeriveMode::New => quote! {
            impl ::mad_hatter::llm::Reflectable for #struct_name {}
            impl ::mad_hatter::llm::Promptable for #struct_name {
                fn to_markdown_depth(&self, _depth: usize) -> ::std::string::String {
                    ::std::string::String::new()
                }
                fn schema_markdown(token: &str) -> ::std::string::String {
                    #schema_body
                }
            }
            impl ::mad_hatter::llm::Parseable for #struct_name {
                fn from_markdown(text: &str, token: &str) -> ::std::result::Result<::std::vec::Vec<Self>, ::std::string::String> {
                    #parse_body
                }
                fn type_name() -> &'static str {
                    #struct_name_str
                }
            }
        },
    }
}

/// Generate schema_markdown body for struct.
fn gen_struct_schema_markdown(struct_name_str: &str, fields: &[FromFieldInfo]) -> TokenStream {
    let mut field_lines = Vec::new();
    for f in fields.iter().filter(|f| f.derived_info.is_none()) {
        let fname = &f.name;

        let fdoc_expr = gen_title_expr(&f.doc, &f.doc_sealed_refs);
        if f.is_vec {
            let vec_type = &f.vec_inner_type;
            if vec_type == "String" {
                field_lines.push(quote! {
                    {
                        let __fd = #fdoc_expr;
                        __out.push_str(&::std::format!("{fname}-{token}\n// {}（每行一个字符串值）\n", __fd, fname = #fname, token = token));
                    }
                });
            } else {
                field_lines.push(quote! {
                    {
                        let __fd = #fdoc_expr;
                        __out.push_str(&::std::format!("{fname}-{token}\n// {}（多个{vtype}元素，用{vtype}-{token}分隔）\n", __fd, fname = #fname, token = token, vtype = #vec_type));
                    }
                });
            }
        } else if f.is_option {
            field_lines.push(quote! {
                {
                    let __fd = #fdoc_expr;
                    __out.push_str(&::std::format!("{fname}-{token}\n{}（可选）\n", __fd, fname = #fname, token = token));
                }
            });
        } else {
            field_lines.push(quote! {
                {
                    let __fd = #fdoc_expr;
                    __out.push_str(&::std::format!("{fname}-{token}\n{}\n", __fd, fname = #fname, token = token));
                }
            });
        }
    }

    quote! {
        let mut __out = ::std::string::String::new();
        __out.push_str(&::std::format!("每个元素以 {sname}-{token} 开头，包含以下字段：\n",
            sname = #struct_name_str, token = token));
        #(#field_lines)*
        __out
    }
}

/// Generate from_markdown body for struct.
fn gen_struct_from_markdown(struct_name: &syn::Ident, struct_name_str: &str, fields: &[FromFieldInfo]) -> TokenStream {
    // Separate parsed fields (from LLM output) and derived fields (computed post-parse)
    let parsed_fields: Vec<&FromFieldInfo> = fields.iter().filter(|f| f.derived_info.is_none()).collect();
    let derived_fields: Vec<&FromFieldInfo> = fields.iter().filter(|f| f.derived_info.is_some()).collect();

    let field_count = parsed_fields.len();

    let mut field_names: Vec<String> = Vec::new();

    for f in &parsed_fields {
        field_names.push(f.name.clone());
    }

    let sep_name_literals: Vec<&str> = field_names.iter().map(|s| s.as_str()).collect();
    let field_optional_tokens: Vec<TokenStream> = parsed_fields.iter().map(|f| {
        if f.is_option { quote!(true) } else { quote!(false) }
    }).collect();

    // Generate field extraction code (similar to gen_multi_field_parse but for struct)
    let mut field_extractions = Vec::new();
    let mut field_var_names: Vec<syn::Ident> = Vec::new();

    for (pi, f) in parsed_fields.iter().enumerate() {
        let i = pi; // parsed index for field_values.get(#i)
        let var_name = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
        let fname = &f.name;


        if f.is_vec {
            if f.vec_inner_type == "String" {
                // Vec<String> field: parse each non-empty, non-comment line as a string
                field_extractions.push(quote! {
                    let #var_name: ::std::vec::Vec<::std::string::String> = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        __raw.lines()
                            .map(|line| line.trim())
                            .filter(|line| !line.is_empty() && !line.starts_with("//"))
                            .map(|line| line.to_string())
                            .collect()
                    };
                });
            } else {
                // Vec<T> field: call T::from_markdown on the extracted text
                let vec_inner_ty = f.vec_inner_syn_type.as_ref().unwrap();
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        <#vec_inner_ty as ::mad_hatter::llm::Parseable>::from_markdown(__raw, token)?
                    };
                });
            }
        } else if f.is_option {
            let inner = &f.inner_type;
            if is_parseable_primitive(inner) {
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| s.trim()).unwrap_or("");
                        if __raw.is_empty() || __raw.starts_with("//") {
                            ::std::option::Option::None
                        } else {
                            match __raw.parse() {
                                Ok(v) => ::std::option::Option::Some(v),
                                Err(e) => return ::std::result::Result::Err(
                                    ::std::format!("[{}] Failed to parse field '{}': {}", #struct_name_str, #fname, e)
                                ),
                            }
                        }
                    };
                });
            } else {
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| s.trim_end()).unwrap_or("");
                        if __raw.is_empty() || __raw.trim_start().starts_with("//") {
                            ::std::option::Option::None
                        } else {
                            ::std::option::Option::Some(__raw.to_string())
                        }
                    };
                });
            }
        } else {
            let fname_str = &f.name;
            if let Some(ref disc_path) = f.discriminate_path {
                let path_expr: syn::ExprPath = syn::parse_str(disc_path).expect("invalid discriminate path");
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        let __val = if #i == #field_count - 1 {
                            __raw.trim_end().to_string()
                        } else {
                            __raw.trim_end_matches('\n').to_string()
                        };
                        #path_expr.discriminate_owned(__val)
                    };
                });
            } else if is_parseable_primitive(&f.inner_type) {
                // 非Option数字字段：必填，parse成数字
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| s.trim()).unwrap_or("");
                        if __raw.is_empty() || __raw.starts_with("//") {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Required field '{}' is empty", #struct_name_str, #fname_str)
                            );
                        }
                        match __raw.parse() {
                            Ok(v) => v,
                            Err(e) => return ::std::result::Result::Err(
                                ::std::format!("[{}] Failed to parse field '{}': {}", #struct_name_str, #fname_str, e)
                            ),
                        }
                    };
                });
            } else {
                field_extractions.push(quote! {
                    let #var_name = {
                        let __raw = __field_values.get(#i).map(|s| *s).unwrap_or("");
                        let __val = if #i == #field_count - 1 {
                            __raw.trim_end().to_string()
                        } else {
                            __raw.trim_end_matches('\n').to_string()
                        };
                        if __val.trim().is_empty() {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Required field '{}' is empty", #struct_name_str, #fname_str)
                            );
                        }
                        __val
                    };
                });
            }
        }

        field_var_names.push(var_name);
    }

    // Regex post-processing for String fields with #[parse(regex = [...])]
    for f in parsed_fields.iter() {
        if !f.regex_patterns.is_empty() && !f.is_vec {
            let var_name = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
            let patterns: Vec<&str> = f.regex_patterns.iter().map(|s| s.as_str()).collect();
            if f.is_option && f.inner_type == "String" {
                field_extractions.push(quote! {
                    let #var_name = #var_name.map(|__v| ::mad_hatter::llm::apply_regex_extraction(&__v, &[#(#patterns),*]));
                });
            } else if !f.is_option {
                field_extractions.push(quote! {
                    let #var_name = ::mad_hatter::llm::apply_regex_extraction(&#var_name, &[#(#patterns),*]);
                });
            }
        }
    }

    // Derived field computation (after all parsed fields including regex post-processing)
    for f in &derived_fields {
        let derived = f.derived_info.as_ref().unwrap();
        let var_name = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
        let source_var = syn::Ident::new(&format!("__field_{}", derived.from_field), proc_macro2::Span::call_site());
        let comparison = match &derived.matches_value {
            DerivedMatchValue::ConstPath(path) => quote! { #source_var == *#path.unseal() },
            DerivedMatchValue::Literal(lit) => quote! { #source_var == #lit },
        };
        field_extractions.push(quote! {
            let #var_name: bool = #comparison;
        });
        field_var_names.push(var_name);
    }

    // Constructor includes all fields (parsed + derived), using __field_{name} variable convention
    let constructor_fields: Vec<TokenStream> = fields.iter().map(|f| {
        let fi = syn::Ident::new(&f.name, proc_macro2::Span::call_site());
        let vn = syn::Ident::new(&format!("__field_{}", f.name), proc_macro2::Span::call_site());
        quote! { #fi: #vn }
    }).collect();


    quote! {
        let __stripped = ::mad_hatter::llm::strip_code_block(text);
        let text = __stripped.as_str();

        let mut __results: ::std::vec::Vec<#struct_name> = ::std::vec::Vec::new();
        let __element_sep = ::std::format!("{}-{}", #struct_name_str, token);

        let __text_trimmed = text.trim();
        let __text_body = __text_trimmed;

        // Split by element separator
        let __sep_with_newline = ::std::format!("{}\n", __element_sep);
        let __chunks: ::std::vec::Vec<&str> = __text_body.split(&__sep_with_newline).collect();

        // Expect: first chunk (before first separator) must be empty
        if let ::std::option::Option::Some(__first_chunk) = __chunks.first() {
            let __first_trimmed = __first_chunk.trim();
            if !__first_trimmed.is_empty() {
                return ::std::result::Result::Err(
                    ::std::format!("[{}] FORMAT VIOLATION: Output REJECTED. You MUST start with the separator `{}`, not garbage text. You wrote: `{}`. NO preamble, NO thinking, NO commentary before the separator. Your output gets thrown away every single time you do this.", #struct_name_str, __element_sep, __first_trimmed)
                );
            }
        }

        for __chunk in __chunks {
            let __chunk = __chunk.trim();
            if __chunk.is_empty() { continue; }

            // For struct, there's no variant name line. The chunk directly contains field separators.
            let __body = __chunk;

            let __seps: ::std::vec::Vec<::std::string::String> = [#(#sep_name_literals),*]
                .iter()
                .map(|n| ::std::format!("{}-{}", n, token))
                .collect();

            let __field_names: &[&str] = &[#(#sep_name_literals),*];
            let __is_optional: &[bool] = &[#(#field_optional_tokens),*];

            // Phase 1: Find all separator positions
            let mut __found: ::std::vec::Vec<(usize, usize, usize)> = ::std::vec::Vec::new();
            for (__fi, __sep) in __seps.iter().enumerate() {
                let __sep_nl = ::std::format!("{}\n", __sep);
                if let Some(__pos) = __body.find(&__sep_nl) {
                    __found.push((__fi, __pos, __pos + __sep_nl.len()));
                } else {
                    let __trimmed = __body.trim_end();
                    if __trimmed.ends_with(__sep.as_str()) {
                        let __sp = __trimmed.len() - __sep.len();
                        __found.push((__fi, __sp, __body.len()));
                    }
                }
            }

            // Phase 2: Sort by position, verify order + required
            __found.sort_by_key(|&(_, __pos, _)| __pos);

            {
                let mut __prev_fi: ::std::option::Option<usize> = ::std::option::Option::None;
                for &(__fi, _, _) in &__found {
                    if let ::std::option::Option::Some(__prev) = __prev_fi {
                        if __fi <= __prev {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Field order violation: '{}' must appear before '{}'",
                                    #struct_name_str, __field_names[__fi], __field_names[__prev])
                            );
                        }
                        for __skipped in (__prev + 1)..__fi {
                            if !__is_optional[__skipped] {
                                return ::std::result::Result::Err(
                                    ::std::format!("[{}] Required field '{}' is missing (expected between '{}' and '{}')",
                                        #struct_name_str, __field_names[__skipped], __field_names[__prev], __field_names[__fi])
                                );
                            }
                        }
                    } else {
                        for __skipped in 0..__fi {
                            if !__is_optional[__skipped] {
                                return ::std::result::Result::Err(
                                    ::std::format!("[{}] Required field '{}' is missing (expected before '{}')",
                                        #struct_name_str, __field_names[__skipped], __field_names[__fi])
                                );
                            }
                        }
                    }
                    __prev_fi = ::std::option::Option::Some(__fi);
                }
                let __last_fi = __prev_fi;
                if let ::std::option::Option::Some(__last) = __last_fi {
                    for __skipped in (__last + 1)..__field_names.len() {
                        if !__is_optional[__skipped] {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Required field '{}' is missing (expected after '{}')",
                                    #struct_name_str, __field_names[__skipped], __field_names[__last])
                            );
                        }
                    }
                } else {
                    for __skipped in 0..__field_names.len() {
                        if !__is_optional[__skipped] {
                            return ::std::result::Result::Err(
                                ::std::format!("[{}] Required field '{}' is missing",
                                    #struct_name_str, __field_names[__skipped])
                            );
                        }
                    }
                }
            }

            // Phase 3: Extract content
            let mut __field_values: ::std::vec::Vec<&str> = ::std::vec::Vec::new();
            __field_values.resize(__seps.len(), "");
            for (__idx, &(__fi, _, __content_start)) in __found.iter().enumerate() {
                let __content_end = if __idx + 1 < __found.len() {
                    __found[__idx + 1].1
                } else {
                    __body.len()
                };
                __field_values[__fi] = &__body[__content_start..__content_end];
            }

            #(#field_extractions)*

            __results.push(#struct_name {
                #(#constructor_fields),*
            });
        }

        ::std::result::Result::Ok(__results)
    }
}

// ============================================================
// Shared helpers
// ============================================================

/// Convert CamelCase to snake_case.
fn to_snake_case(s: &str) -> String {
    let mut result = String::new();
    for (i, c) in s.chars().enumerate() {
        if c.is_uppercase() {
            if i > 0 {
                result.push('_');
            }
            result.push(c.to_lowercase().next().unwrap());
        } else {
            result.push(c);
        }
    }
    result
}

/// Check if a type is `Option<T>` and return (is_option, inner_type_name).
fn check_option_type(ty: &syn::Type) -> (bool, String) {
    if let syn::Type::Path(type_path) = ty {
        let last_seg = type_path.path.segments.last();
        if let Some(seg) = last_seg {
            if seg.ident == "Option" {
                if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
                    if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
                        let inner_str = quote!(#inner_ty).to_string().replace(' ', "");
                        return (true, inner_str);
                    }
                }
            }
            // 非Option：也返回裸type name（如 "u64" / "String"），便于下游按类型选parse策略
            let bare = seg.ident.to_string();
            return (false, bare);
        }
    }
    (false, String::new())
}

/// Check if a type is `Vec<T>` and return (is_vec, inner_type_name, inner_syn_type).
fn check_vec_type(ty: &syn::Type) -> (bool, String, Option<syn::Type>) {
    if let syn::Type::Path(type_path) = ty {
        let last_seg = type_path.path.segments.last();
        if let Some(seg) = last_seg {
            if seg.ident == "Vec" {
                if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
                    if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() {
                        let inner_str = quote!(#inner_ty).to_string().replace(' ', "");
                        return (true, inner_str, Some(inner_ty.clone()));
                    }
                }
            }
        }
    }
    (false, String::new(), None)
}

/// Check if an inner type name is a numeric type (for Option<numeric> parsing).
fn is_numeric_inner_type(inner: &str) -> bool {
    matches!(inner, "u8" | "u16" | "u32" | "u64" | "usize" |
                    "i8" | "i16" | "i32" | "i64" | "isize" |
                    "f32" | "f64")
}

/// Check if an inner type supports FromStr-based parsing (numeric types + bool).
/// Used for fields that need str::parse() with Result<T, E> semantics.
fn is_parseable_primitive(inner: &str) -> bool {
    is_numeric_inner_type(inner) || inner == "bool"
}

fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
    let mut docs = Vec::new();
    for attr in attrs {
        if attr.path().is_ident("doc") {
            if let Meta::NameValue(nv) = &attr.meta {
                if let syn::Expr::Lit(expr_lit) = &nv.value {
                    if let Lit::Str(s) = &expr_lit.lit {
                        let trimmed = s.value().trim().to_string();
                        if let Some(rest) = trimmed.strip_prefix("@render ") {
                            docs.push(rest.to_string());
                        } else if trimmed == "@render" {
                            docs.push(String::new());
                        }
                    }
                }
            }
        }
    }
    docs
}

#[allow(dead_code)]
/// Join doc comments as comment lines with "// " prefix, preserving original line breaks.
fn join_as_comment(docs: &[String]) -> String {
    docs.iter().map(|d| format!("// {}", d)).collect::<Vec<_>>().join("\n")
}
/// Check if a field has `#[markdown(skip)]` attribute.
fn has_markdown_skip(attrs: &[syn::Attribute]) -> bool {
    for attr in attrs {
        if attr.path().is_ident("markdown") {
            if let Ok(nested) = attr.parse_args::<syn::Ident>() {
                if nested == "skip" {
                    return true;
                }
            }
        }
    }
    false
}

/// Check if a field has `#[markdown(required)]` attribute.
fn has_markdown_required(attrs: &[syn::Attribute]) -> bool {
    for attr in attrs {
        if attr.path().is_ident("markdown") {
            if let Ok(nested) = attr.parse_args::<syn::Ident>() {
                if nested == "required" {
                    return true;
                }
            }
        }
    }
    false
}
/// Check if a field has `#[markdown(flatten)]` attribute.
fn has_markdown_flatten(attrs: &[syn::Attribute]) -> bool {
    for attr in attrs {
        if attr.path().is_ident("markdown") {
            if let Ok(nested) = attr.parse_args::<syn::Ident>() {
                if nested == "flatten" {
                    return true;
                }
            }
        }
    }
    false
}

/// Extract discriminate path from `#[markdown(discriminate = "path::to::SEALED")]` attribute.
fn get_markdown_discriminate(attrs: &[syn::Attribute]) -> Option<String> {
    for attr in attrs {
        if attr.path().is_ident("markdown") {
            if let Ok(nv) = attr.parse_args::<syn::MetaNameValue>() {
                if nv.path.is_ident("discriminate") {
                    if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(s), .. }) = &nv.value {
                        return Some(s.value());
                    }
                }
            }
        }
    }
    None
}

/// Check if a field has `#[markdown(weak)]` attribute.
fn has_markdown_weak(attrs: &[syn::Attribute]) -> bool {
    for attr in attrs {
        if attr.path().is_ident("markdown") {
            if let Ok(nested) = attr.parse_args::<syn::Ident>() {
                if nested == "weak" {
                    return true;
                }
            }
        }
    }
    false
}

/// Check for `#[prompt(volatile)]` attribute on a field.
fn has_prompt_volatile(attrs: &[syn::Attribute]) -> bool {
    for attr in attrs {
        if attr.path().is_ident("prompt") {
            if let Ok(ident) = attr.parse_args::<syn::Ident>() {
                if ident == "volatile" {
                    return true;
                }
            }
        }
    }
    false
}

/// Extract `#[prompt(date_format = "...")]` attribute value.
fn extract_date_format(attrs: &[syn::Attribute]) -> Option<String> {
    for attr in attrs {
        if attr.path().is_ident("prompt") {
            let result = attr.parse_args_with(|input: syn::parse::ParseStream| {
                let ident: syn::Ident = input.parse()?;
                if ident != "date_format" {
                    return Err(input.error("not date_format"));
                }
                let _: syn::Token![=] = input.parse()?;
                let lit: syn::LitStr = input.parse()?;
                Ok(lit.value())
            });
            if let Ok(fmt) = result {
                return Some(fmt);
            }
        }
    }
    None
}


/// Extract `#[prompt(format = "...")]` attribute from struct-level attributes.
fn extract_prompt_format(attrs: &[syn::Attribute]) -> Option<String> {
    for attr in attrs {
        if attr.path().is_ident("prompt") {
            let result = attr.parse_args_with(|input: syn::parse::ParseStream| {
                let ident: syn::Ident = input.parse()?;
                if ident != "format" {
                    return Err(input.error("not format"));
                }
                let _: syn::Token![=] = input.parse()?;
                let lit: syn::LitStr = input.parse()?;
                Ok(lit.value())
            });
            if let Ok(fmt) = result {
                return Some(fmt);
            }
        }
    }
    None
}

/// Parse a format string like `"{timestamp}_{short_id}"` into a format template and field idents.
/// Returns (template, field_idents) where template has `{}` placeholders.
fn parse_format_string(fmt: &str) -> (String, Vec<proc_macro2::Ident>) {
    let mut template = String::new();
    let mut fields = Vec::new();
    let mut chars = fmt.chars().peekable();
    while let Some(c) = chars.next() {
        if c == '{' {
            let mut field_name = String::new();
            while let Some(&nc) = chars.peek() {
                if nc == '}' {
                    chars.next();
                    break;
                }
                field_name.push(nc);
                chars.next();
            }
            template.push_str("{}");
            fields.push(proc_macro2::Ident::new(&field_name, proc_macro2::Span::call_site()));
        } else {
            template.push(c);
        }
    }
    (template, fields)
}

/// Parse `#[parse(regex = [r"...", r"..."])]` attribute, returning the list of patterns.
fn extract_parse_regex(attrs: &[syn::Attribute]) -> Vec<String> {
    for attr in attrs {
        if !attr.path().is_ident("parse") {
            continue;
        }
        if let syn::Meta::List(meta_list) = &attr.meta {
            if let Ok(parsed) = syn::parse2::<ParseRegexAttr>(meta_list.tokens.clone()) {
                return parsed.patterns;
            }
        }
    }
    Vec::new()
}

struct ParseRegexAttr {
    patterns: Vec<String>,
}

impl syn::parse::Parse for ParseRegexAttr {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let ident: syn::Ident = input.parse()?;
        if ident != "regex" {
            return Err(syn::Error::new(ident.span(), "expected `regex`"));
        }
        let _: syn::Token![=] = input.parse()?;
        let content;
        syn::bracketed!(content in input);
        let mut patterns = Vec::new();
        while !content.is_empty() {
            let lit: syn::LitStr = content.parse()?;
            patterns.push(lit.value());
            if content.is_empty() {
                break;
            }
            let _: syn::Token![,] = content.parse()?;
        }
        Ok(ParseRegexAttr { patterns })
    }
}

/// The matches value in a derived attribute: constant path or string literal.
enum DerivedMatchValue {
    /// `matches = crate::bindings::SENDER_USER` — compared via `.unseal()`
    ConstPath(syn::Path),
    /// `matches = "user"` — compared directly
    Literal(String),
}

/// Information about a derived field: `#[derived(from = field_name, matches = ...)]`
struct DerivedInfo {
    from_field: String,
    matches_value: DerivedMatchValue,
}

/// Parse `#[derived(from = field_name, matches = CONST_PATH | "literal")]` attribute.
fn extract_derived_attr(attrs: &[syn::Attribute]) -> Option<DerivedInfo> {
    for attr in attrs {
        if !attr.path().is_ident("derived") {
            continue;
        }
        if let syn::Meta::List(meta_list) = &attr.meta {
            if let Ok(parsed) = syn::parse2::<DerivedAttrArgs>(meta_list.tokens.clone()) {
                return Some(DerivedInfo {
                    from_field: parsed.from_field,
                    matches_value: parsed.matches_value,
                });
            }
        }
    }
    None
}

/// Check if a field has `#[derived(...)]` attribute.
fn has_derived_attr(attrs: &[syn::Attribute]) -> bool {
    attrs.iter().any(|a| a.path().is_ident("derived"))
}

/// Parser for `from = field_name, matches = CONST_PATH | "literal"` inside #[derived(...)].
struct DerivedAttrArgs {
    from_field: String,
    matches_value: DerivedMatchValue,
}

impl syn::parse::Parse for DerivedAttrArgs {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        // Parse: from = field_name
        let from_ident: syn::Ident = input.parse()?;
        if from_ident != "from" {
            return Err(syn::Error::new(from_ident.span(), "expected `from`"));
        }
        let _: syn::Token![=] = input.parse()?;
        let from_field_ident: syn::Ident = input.parse()?;
        let from_field = from_field_ident.to_string();

        let _: syn::Token![,] = input.parse()?;

        // Parse: matches = CONST_PATH | "literal"
        let matches_ident: syn::Ident = input.parse()?;
        if matches_ident != "matches" {
            return Err(syn::Error::new(matches_ident.span(), "expected `matches`"));
        }
        let _: syn::Token![=] = input.parse()?;

        let matches_value = if input.peek(syn::LitStr) {
            let lit: syn::LitStr = input.parse()?;
            DerivedMatchValue::Literal(lit.value())
        } else {
            let path: syn::Path = input.parse()?;
            DerivedMatchValue::ConstPath(path)
        };

        Ok(DerivedAttrArgs { from_field, matches_value })
    }
}
