//! Code generation helpers for `#[derive(State)]`.
//!
//! Generates:
//! 1. `{Name}Update` companion struct with `Option<T>` fields
//! 2. `impl State for {Name}` with channel-aware `apply()`
//! 3. `impl CoreState for {Name}` when `#[core]` fields are present
//! 4. Builder methods on the Update struct

use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Ident, Type, Visibility};

use crate::field_attr::{ChannelKind, FieldAttrs};
use crate::path_resolution::MacroPaths;

/// A parsed field with its attributes resolved.
pub struct ParsedField<'a> {
    pub name: &'a Ident,
    pub ty: &'a Type,
    pub attrs: FieldAttrs,
}

/// Generate the `{Name}Update` struct with `Option<T>` for every field.
///
/// # REVIEW(002): Visibility matching
/// The generated Update struct inherits the visibility of the source State struct.
/// A `pub(crate) struct Foo` produces `pub(crate) struct FooUpdate`, preventing
/// type leakage beyond the intended module boundary.
pub fn gen_update_struct(
    state_name: &Ident,
    vis: &Visibility,
    fields: &[ParsedField],
    paths: &MacroPaths,
) -> TokenStream {
    let update_name = format_ident!("{}Update", state_name);
    let core = &paths.core;
    let serde = &paths.serde;
    let serde_crate = &paths.serde_crate;

    let update_fields: Vec<_> = fields
        .iter()
        .map(|f| {
            let name = f.name;
            let ty = f.ty;
            quote! { pub #name: Option<#ty> }
        })
        .collect();

    let builder_methods: Vec<_> = fields
        .iter()
        .map(|f| {
            let name = f.name;
            let ty = f.ty;
            let method_name = format_ident!("with_{}", name);
            quote! {
                pub fn #method_name(mut self, v: #ty) -> Self {
                    self.#name = Some(v);
                    self
                }
            }
        })
        .collect();

    quote! {
        #[derive(Clone, Debug, Default, #serde::Serialize, #serde::Deserialize)]
        #[serde(crate = #serde_crate)]
        #vis struct #update_name {
            #(#update_fields,)*
        }

        impl #core::StateUpdate for #update_name {}

        impl #update_name {
            pub fn new() -> Self {
                Default::default()
            }

            #(#builder_methods)*
        }
    }
}

/// Generate `impl State for {Name}` with channel-aware `apply()`.
pub fn gen_state_impl(
    state_name: &Ident,
    fields: &[ParsedField],
    paths: &MacroPaths,
) -> TokenStream {
    let update_name = format_ident!("{}Update", state_name);
    let core = &paths.core;

    let apply_arms: Vec<_> = fields
        .iter()
        .map(|f| {
            let name = f.name;
            let is_messages_core = f.attrs.is_core && name == "messages";

            let name_str = name.to_string();
            match f.attrs.channel {
                _ if is_messages_core => {
                    // Special: messages field uses add_messages reducer
                    quote! {
                        if let Some(msgs) = update.#name {
                            #core::add_messages(&mut self.#name, msgs);
                        }
                    }
                }
                ChannelKind::LastValue | ChannelKind::Ephemeral => {
                    // Overwrite if Some
                    quote! {
                        if let Some(v) = update.#name {
                            self.#name = v;
                        }
                    }
                }
                ChannelKind::BroadcastLast => {
                    // Overwrite if Some, log when overwritten for debugging
                    quote! {
                        if let Some(v) = update.#name {
                            #[cfg(debug_assertions)]
                            {
                                eprintln!(
                                    "[BroadcastLast] field `{}` overwritten",
                                    #name_str
                                );
                            }
                            self.#name = v;
                        }
                    }
                }
                ChannelKind::Appender | ChannelKind::Topic => {
                    // Extend if Some
                    quote! {
                        if let Some(v) = update.#name {
                            self.#name.extend(v);
                        }
                    }
                }
            }
        })
        .collect();

    quote! {
        impl #core::State for #state_name {
            type Update = #update_name;

            fn apply(&mut self, update: #update_name) {
                #(#apply_arms)*
            }
        }
    }
}

/// Generate `impl CoreState for {Name}` when `#[core]` fields are present.
/// Returns `None` if no `#[core]` fields exist.
///
/// # REVIEW(002): Core field validation
/// When any field has `#[core]`, we validate that ALL four required CoreState
/// fields exist (`messages`, `iterations`, `thread_id`, `context`). Without
/// this check, the macro would generate code referencing `self.iterations` etc.
/// which would fail with an opaque rustc error inside generated code — terrible DX.
/// This validation gives users a clear, actionable compile error instead.
pub fn gen_core_state_impl(
    state_name: &Ident,
    fields: &[ParsedField],
    paths: &MacroPaths,
) -> Option<TokenStream> {
    let core = &paths.core;
    let core_fields: Vec<_> = fields.iter().filter(|f| f.attrs.is_core).collect();
    if core_fields.is_empty() {
        return None;
    }

    // Validate all required CoreState fields are present
    let core_field_names: Vec<String> = core_fields.iter().map(|f| f.name.to_string()).collect();
    let required = ["messages", "iterations", "thread_id", "context"];
    let missing: Vec<&str> = required
        .iter()
        .filter(|r| !core_field_names.contains(&r.to_string()))
        .copied()
        .collect();

    if !missing.is_empty() {
        let missing_list = missing.join(", ");
        let err_msg = format!(
            "#[core] fields detected but CoreState requires all four fields: \
             messages: Vec<Message>, iterations: u32, thread_id: String, context: ExecutionContext. \
             Missing: {missing_list}"
        );
        // Emit compile error on the struct name for clear diagnostics
        return Some(quote! {
            compile_error!(#err_msg);
        });
    }

    Some(quote! {
        impl #core::CoreState for #state_name {
            fn messages(&self) -> &[#core::Message] {
                &self.messages
            }

            fn messages_mut(&mut self) -> &mut Vec<#core::Message> {
                &mut self.messages
            }

            fn iterations(&self) -> u32 {
                self.iterations
            }

            fn set_iterations(&mut self, n: u32) {
                self.iterations = n;
            }

            fn thread_id(&self) -> &str {
                &self.thread_id
            }

            fn context(&self) -> &#core::state::ExecutionContext {
                &self.context
            }

            fn context_mut(&mut self) -> &mut #core::state::ExecutionContext {
                &mut self.context
            }
        }
    })
}
