//! Integration tests for `#[derive(State)]`.
//!
//! Verifies that the derive macro generates correct Update structs,
//! channel-aware `apply()` logic, and CoreState wiring.

use pe_core::Message;
use pe_core::state::{CoreState, ExecutionContext, State, StateUpdate};
use pe_macros::State;
use serde::{Deserialize, Serialize};

// ── All channel types in one struct ──────────────────────────────────

#[derive(State, Clone, Debug, Serialize, Deserialize)]
struct MultiChannelState {
    /// Default: LastValue (overwrite)
    name: String,

    #[channel(LastValue)]
    explicit_last: u32,

    #[channel(BroadcastLast)]
    broadcast_field: String,

    #[channel(Appender)]
    logs: Vec<String>,

    #[channel(Ephemeral)]
    scratch: String,

    #[channel(Topic)]
    events: Vec<String>,
}

#[test]
fn test_update_struct_defaults_to_none() {
    let update = MultiChannelStateUpdate::new();
    assert!(update.name.is_none());
    assert!(update.explicit_last.is_none());
    assert!(update.broadcast_field.is_none());
    assert!(update.logs.is_none());
    assert!(update.scratch.is_none());
    assert!(update.events.is_none());
}

#[test]
fn test_last_value_overwrites() {
    let mut state = MultiChannelState {
        name: "old".into(),
        explicit_last: 1,
        broadcast_field: "old".into(),
        logs: vec![],
        scratch: String::new(),
        events: vec![],
    };

    state.apply(MultiChannelStateUpdate::new().with_name("new".into()));
    assert_eq!(state.name, "new");

    state.apply(MultiChannelStateUpdate::new().with_explicit_last(42));
    assert_eq!(state.explicit_last, 42);
}

#[test]
fn test_broadcast_last_overwrites() {
    let mut state = MultiChannelState {
        name: String::new(),
        explicit_last: 0,
        broadcast_field: "first".into(),
        logs: vec![],
        scratch: String::new(),
        events: vec![],
    };

    state.apply(MultiChannelStateUpdate::new().with_broadcast_field("second".into()));
    assert_eq!(state.broadcast_field, "second");
}

#[test]
fn test_appender_extends() {
    let mut state = MultiChannelState {
        name: String::new(),
        explicit_last: 0,
        broadcast_field: String::new(),
        logs: vec!["first".into()],
        scratch: String::new(),
        events: vec![],
    };

    state.apply(MultiChannelStateUpdate::new().with_logs(vec!["second".into(), "third".into()]));
    assert_eq!(state.logs, vec!["first", "second", "third"]);
}

#[test]
fn test_topic_extends() {
    let mut state = MultiChannelState {
        name: String::new(),
        explicit_last: 0,
        broadcast_field: String::new(),
        logs: vec![],
        scratch: String::new(),
        events: vec!["e1".into()],
    };

    state.apply(MultiChannelStateUpdate::new().with_events(vec!["e2".into()]));
    assert_eq!(state.events, vec!["e1", "e2"]);
}

#[test]
fn test_ephemeral_overwrites() {
    let mut state = MultiChannelState {
        name: String::new(),
        explicit_last: 0,
        broadcast_field: String::new(),
        logs: vec![],
        scratch: "old".into(),
        events: vec![],
    };

    state.apply(MultiChannelStateUpdate::new().with_scratch("new".into()));
    assert_eq!(state.scratch, "new");
}

#[test]
fn test_builder_chaining() {
    let update = MultiChannelStateUpdate::new()
        .with_name("test".into())
        .with_explicit_last(99)
        .with_logs(vec!["entry".into()]);

    assert_eq!(update.name, Some("test".into()));
    assert_eq!(update.explicit_last, Some(99));
    assert_eq!(update.logs, Some(vec!["entry".into()]));
    assert!(update.broadcast_field.is_none());
}

// ── CoreState wiring ─────────────────────────────────────────────────

#[derive(State, Clone, Debug, Serialize, Deserialize)]
struct FullCoreState {
    #[core]
    #[channel(Appender)]
    messages: Vec<Message>,

    #[core]
    iterations: u32,

    #[core]
    thread_id: String,

    #[core]
    context: ExecutionContext,

    /// Non-core field alongside core fields
    custom_data: String,
}

#[test]
fn test_core_state_messages_accessor() {
    let state = FullCoreState {
        messages: vec![],
        iterations: 0,
        thread_id: "t1".into(),
        context: ExecutionContext::new("test-agent"),
        custom_data: "hello".into(),
    };

    assert_eq!(state.messages().len(), 0);
    assert_eq!(state.iterations(), 0);
    assert_eq!(state.thread_id(), "t1");
}

#[test]
fn test_core_state_thread_id_wiring() {
    let state = FullCoreState {
        messages: vec![],
        iterations: 5,
        thread_id: "thread-abc".into(),
        context: ExecutionContext::new("test-agent"),
        custom_data: String::new(),
    };

    assert_eq!(state.thread_id(), "thread-abc");
    assert_eq!(state.iterations(), 5);
}

#[test]
fn test_core_state_set_iterations() {
    let mut state = FullCoreState {
        messages: vec![],
        iterations: 0,
        thread_id: "t1".into(),
        context: ExecutionContext::new("test-agent"),
        custom_data: String::new(),
    };

    state.set_iterations(42);
    assert_eq!(state.iterations(), 42);
}

#[test]
fn test_state_update_is_state_update_trait() {
    // Verify the generated Update type implements StateUpdate
    fn assert_state_update<T: StateUpdate>() {}
    assert_state_update::<MultiChannelStateUpdate>();
    assert_state_update::<FullCoreStateUpdate>();
}
