//! Integration tests for the `interrupt!` macro.
//!
//! Verifies that `interrupt!` expands to correct `NodeResult::Interrupt`
//! returns in both simple and partial-update forms.

use pe_core::node::{NodeContext, NodeFn, NodeResult};
use pe_core::state::{State, StateUpdate};
use pe_macros::{interrupt, node};
use serde::{Deserialize, Serialize};

// ── Test State ───────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct IntState {
    value: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
struct IntStateUpdate {
    value: Option<String>,
}

impl StateUpdate for IntStateUpdate {}

impl State for IntState {
    type Update = IntStateUpdate;
    fn apply(&mut self, update: IntStateUpdate) {
        if let Some(v) = update.value {
            self.value = v;
        }
    }
}

fn test_ctx(name: &str) -> NodeContext {
    NodeContext {
        step: 1,
        recursion_limit: 10,
        node_name: name.into(),
        activation: pe_core::node::ActivationReason::EntryPoint,
        metadata: Default::default(),
        phase_store: Default::default(),
        stream_sender: None,
        tool_observer: None,
        lobe_runtime_service_factory: None,
    }
}

// ── Simple interrupt (reason only) ───────────────────────────────────

#[node]
async fn needs_approval(_state: &IntState) -> NodeResult<IntStateUpdate> {
    interrupt!("Human approval required");
}

#[tokio::test]
async fn test_interrupt_simple_reason() {
    let state = IntState {
        value: "draft".into(),
    };
    let result = needs_approval
        .call(&state, &test_ctx("needs_approval"))
        .await;

    match result {
        NodeResult::Interrupt(req) => {
            assert_eq!(req.reason, "Human approval required");
            assert!(
                req.partial_update.is_none(),
                "Simple interrupt should have no partial update"
            );
            assert_eq!(
                req.resume_point, "Human approval required",
                "Resume point defaults to reason string"
            );
        }
        other => panic!("Expected Interrupt, got: {other:?}"),
    }
}

// ── Interrupt with partial state update ──────────────────────────────

#[node]
async fn needs_review(state: &IntState) -> NodeResult<IntStateUpdate> {
    let draft = format!("reviewed:{}", state.value);
    interrupt!("Review needed", partial: IntStateUpdate {
        value: Some(draft),
    });
}

#[tokio::test]
async fn test_interrupt_with_partial_update() {
    let state = IntState {
        value: "original".into(),
    };
    let result = needs_review.call(&state, &test_ctx("needs_review")).await;

    match result {
        NodeResult::Interrupt(req) => {
            assert_eq!(req.reason, "Review needed");
            let partial = req
                .partial_update
                .expect("Partial update should be present");
            assert_eq!(partial.value, Some("reviewed:original".into()));
        }
        other => panic!("Expected Interrupt, got: {other:?}"),
    }
}
