//! `#[derive(State)]` proc macro implementation.
//!
//! Generates:
//! - `{Name}Update` companion struct with `Option<T>` per field
//! - `impl State for {Name}` with channel-aware `apply()`
//! - `impl CoreState for {Name}` when `#[core]` fields are present
//! - Builder methods (`with_*`) on the update struct
//!
//! Uses `syn::Error::into_compile_error` for all error paths (pattern P7).

use proc_macro2::TokenStream;
use syn::{Data, DeriveInput, Error, Fields, Result};

use crate::codegen::{ParsedField, gen_core_state_impl, gen_state_impl, gen_update_struct};
use crate::field_attr::parse_field_attrs;
use crate::path_resolution::MacroPaths;

/// Entry point for `#[derive(State)]`.
pub fn derive_state_impl(input: DeriveInput) -> Result<TokenStream> {
    let state_name = &input.ident;
    // NOTE: Capture visibility so the generated Update struct matches.
    // Without this, a `pub(crate) struct MyState` would generate a `pub struct MyStateUpdate`,
    // leaking the type beyond the intended scope.
    let vis = &input.vis;

    // Only works on structs with named fields
    let fields = match &input.data {
        Data::Struct(data) => match &data.fields {
            Fields::Named(named) => &named.named,
            _ => {
                return Err(Error::new_spanned(
                    state_name,
                    "#[derive(State)] only supports structs with named fields",
                ));
            }
        },
        _ => {
            return Err(Error::new_spanned(
                state_name,
                "#[derive(State)] only supports structs, not enums or unions",
            ));
        }
    };

    if fields.is_empty() {
        return Err(Error::new_spanned(
            state_name,
            "#[derive(State)] requires at least one field",
        ));
    }

    // Parse field attributes
    let parsed_fields: Vec<ParsedField> = fields
        .iter()
        .map(|field| {
            let name = field
                .ident
                .as_ref()
                .expect("named fields always have idents");
            let attrs = parse_field_attrs(&field.attrs)?;
            Ok(ParsedField {
                name,
                ty: &field.ty,
                attrs,
            })
        })
        .collect::<Result<Vec<_>>>()?;

    // Generate code
    let paths = MacroPaths::new();
    let update_struct = gen_update_struct(state_name, vis, &parsed_fields, &paths);
    let state_impl = gen_state_impl(state_name, &parsed_fields, &paths);
    let core_state_impl = gen_core_state_impl(state_name, &parsed_fields, &paths);

    let mut output = TokenStream::new();
    output.extend(update_struct);
    output.extend(state_impl);
    if let Some(core_impl) = core_state_impl {
        output.extend(core_impl);
    }

    Ok(output)
}
