//! # pe-macros — Procedural macros for Potential Expectations
//!
//! Provides derive and attribute macros that reduce boilerplate when
//! defining agents, tools, and state types. Depends on `pe-core` for
//! the trait surface that the generated code must satisfy.
//!
//! This crate is a `proc-macro` crate and cannot export non-macro items.
//! Generated code resolves through the `potential-expectations` facade when
//! that crate is present, and otherwise falls back to direct leaf crates such
//! as `pe-core`.
//!
//! Facade users can import macros from `potential_expectations::prelude::*`.
//! Because user state types must also implement serde traits, facade-only state
//! structs should use `#[serde(crate = "potential_expectations::serde")]`.

extern crate proc_macro;

mod codegen;
mod derive_state;
mod field_attr;
mod interrupt;
mod node_attr;
mod node_return;
mod path_resolution;
// Mode 3 `node!` DSL infrastructure — parsed and tested but not yet wired to a
// proc-macro entry point. Items appear unused outside of tests.
#[cfg(feature = "node-dsl")]
#[allow(dead_code)]
mod node_codegen;
#[cfg(feature = "node-dsl")]
#[allow(dead_code)]
mod node_dsl;

use proc_macro::TokenStream;

/// Derive the `State` trait for a struct.
///
/// Generates:
/// - A companion `{Name}Update` 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
///
/// # Field Attributes
///
/// - `#[channel(LastValue)]` — overwrite on update (default if no annotation)
/// - `#[channel(BroadcastLast)]` — overwrite on update, logs when overwritten (debug aid)
/// - `#[channel(Appender)]` — extend/append on update
/// - `#[channel(Ephemeral)]` — overwrite on update, cleared between supersteps
/// - `#[channel(Topic)]` — extend on update, cleared between supersteps
/// - `#[core]` — marks a field as part of `CoreState` (triggers `CoreState` impl generation)
///
/// # Example
///
/// ```ignore
/// use pe_macros::State;
///
/// #[derive(State, Clone, Debug, serde::Serialize, serde::Deserialize)]
/// struct MyState {
///     #[channel(Appender)]
///     messages: Vec<String>,
///
///     topic: String,  // defaults to LastValue
/// }
///
/// // Generated: MyStateUpdate, impl State for MyState, builder methods
/// let update = MyStateUpdate::new().with_topic("rust".into());
/// ```
#[proc_macro_derive(State, attributes(channel, core))]
pub fn derive_state(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    match derive_state::derive_state_impl(input) {
        Ok(tokens) => tokens.into(),
        Err(err) => err.into_compile_error().into(),
    }
}

/// Transform an async function into a `NodeFn` implementation.
///
/// Eliminates the boilerplate of defining a struct + `impl NodeFn` manually.
/// The function name becomes the node name (used in logging and `add_node`).
///
/// # Mode 1: Simple (no context)
///
/// ```ignore
/// use pe_macros::node;
/// use pe_core::node::NodeResult;
///
/// #[node]
/// async fn prepare(state: &MyState) -> NodeResult<MyStateUpdate> {
///     NodeResult::Update(MyStateUpdate { /* ... */ })
/// }
///
/// // Use: graph.add_node("prepare", prepare)
/// ```
///
/// # Limitations
///
/// - Only concrete types are supported (no generics like `fn node<S: State>`).
/// - The function must be `async` and return `NodeResult<ConcreteUpdateType>`
///   or `Result<NodeResult<ConcreteUpdateType>, PeError>` (for `?` operator support).
///
/// # Mode 2: With NodeContext
///
/// ```ignore
/// #[node]
/// async fn chat(state: &MyState, ctx: &NodeContext) -> NodeResult<MyStateUpdate> {
///     if ctx.is_last_step() { /* wrap up */ }
///     NodeResult::Update(MyStateUpdate { /* ... */ })
/// }
/// ```
///
/// # Generated code
///
/// - A unit struct `{FnName}Node` (PascalCase from snake_case)
/// - `impl NodeFn<StateType> for {FnName}Node`
/// - A `const` binding with the original function name holding the struct instance
#[proc_macro_attribute]
pub fn node(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let item = syn::parse_macro_input!(item as syn::ItemFn);
    match node_attr::node_attr_impl(item) {
        Ok(tokens) => tokens.into(),
        Err(err) => err.into_compile_error().into(),
    }
}

/// Durable suspension point for human-in-the-loop workflows.
///
/// Expands to `return NodeResult::Interrupt(...)`. Use inside `node!` blocks
/// or manually-implemented `NodeFn::call` methods.
///
/// # Forms
///
/// ```ignore
/// // Simple interrupt — reason only
/// interrupt!("Please review the draft");
///
/// // With partial state update
/// interrupt!("Review needed", partial: MyUpdate { draft: Some(text) });
/// ```
#[proc_macro]
pub fn interrupt(input: TokenStream) -> TokenStream {
    let parsed = syn::parse_macro_input!(input as interrupt::InterruptInput);
    interrupt::expand_interrupt(parsed).into()
}

// NOTE: `node!` DSL (Mode 3) is available via `node_dsl` and `node_codegen` modules
// (behind the `node-dsl` feature flag) but is not registered as a standalone
// proc macro because it requires context (node name, state type, update type)
// that must be provided externally. The `#[node]` attribute macro (Mode 1/2)
// is the primary user-facing API.
