//! `interrupt!` macro implementation -- durable suspension point for HITL.
//!
//! Expands to a `return NodeResult::Interrupt(...)` expression.
//! Supports two forms:
//!
//! - `interrupt!("reason")` -- interrupt with no partial update
//! - `interrupt!("reason", partial: expr)` -- interrupt with partial state update

use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Expr, LitStr, Token};

use crate::path_resolution::MacroPaths;

/// Parsed input for the `interrupt!` macro.
pub(crate) struct InterruptInput {
    /// The reason string shown to the human.
    reason: LitStr,
    /// Optional partial update expression.
    partial_update: Option<Expr>,
}

impl Parse for InterruptInput {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let reason: LitStr = input.parse()?;

        let partial_update = if input.peek(Token![,]) {
            input.parse::<Token![,]>()?;
            // Parse `partial: expr`
            let ident: syn::Ident = input.parse()?;
            if ident != "partial" {
                return Err(syn::Error::new(
                    ident.span(),
                    "expected `partial:` after reason string",
                ));
            }
            input.parse::<Token![:]>()?;
            let expr: Expr = input.parse()?;
            Some(expr)
        } else {
            None
        };

        Ok(Self {
            reason,
            partial_update,
        })
    }
}

/// Generate the interrupt expression.
pub(crate) fn expand_interrupt(input: InterruptInput) -> TokenStream {
    let reason = &input.reason;
    let reason_str = reason.value();
    let paths = MacroPaths::new();
    let core = &paths.core;

    let partial = match &input.partial_update {
        Some(expr) => quote! { Some(#expr) },
        None => quote! { None },
    };

    // Use the reason string as the resume point identifier.
    // In Mode 2/3 macros, the resume_point is set to "node_name:phase_index"
    // but for standalone interrupt!, the reason string is unique enough.
    let resume_point = reason_str;

    quote! {
        return #core::node::NodeResult::Interrupt(
            #core::node::InterruptRequest {
                reason: #reason.to_string(),
                partial_update: #partial,
                resume_point: #resume_point.to_string(),
            }
        )
    }
}
