use ratatui::{buffer::Buffer, layout::Rect};
/// Integration tests for the textual-rs-macros proc-macro crate.
///
/// Tests verify that #[derive(Widget)] and #[widget_impl] work correctly:
/// - widget_type_name returns struct name
/// - #[focusable] makes can_focus() return true
/// - on_mount wires own_id, on_unmount clears it
/// - #[on(T)] dispatches events via on_event
/// - #[keybinding("key", "action")] wires key_bindings and on_action
use std::any::Any;
use std::cell::Cell;
use textual_rs::widget::context::AppContext;
use textual_rs::widget::{EventPropagation, Widget, WidgetId};

// ---------------------------------------------------------------------------
// Test 1: Basic derive + widget_impl generates widget_type_name and on_mount
// ---------------------------------------------------------------------------

#[derive(textual_rs::Widget)]
struct BasicWidget {
    own_id: Cell<Option<WidgetId>>,
}

impl BasicWidget {
    fn new() -> Self {
        Self {
            own_id: Cell::new(None),
        }
    }
}

/// #[widget_impl] injects widget_type_name, can_focus, on_mount, on_unmount
/// by delegating to the __ helpers generated by #[derive(Widget)].
#[textual_rs::widget_impl]
impl Widget for BasicWidget {
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}
}

#[test]
fn derive_widget_basic() {
    let w = BasicWidget::new();
    assert_eq!(w.widget_type_name(), "BasicWidget");
    assert!(!w.can_focus());
}

#[test]
fn derive_widget_on_mount_sets_id() {
    let w = BasicWidget::new();
    assert!(w.own_id.get().is_none());

    // Create a minimal context and insert a widget to get a valid WidgetId
    let mut ctx = AppContext::new();
    let id = ctx
        .arena
        .insert(Box::new(BasicWidget::new()) as Box<dyn Widget>);

    w.on_mount(id);
    assert_eq!(w.own_id.get(), Some(id));

    w.on_unmount(id);
    assert!(w.own_id.get().is_none());
}

// ---------------------------------------------------------------------------
// Test 2: Focusable derive
// ---------------------------------------------------------------------------

#[derive(textual_rs::Widget)]
#[focusable]
struct FocusableWidget {
    own_id: Cell<Option<WidgetId>>,
}

#[textual_rs::widget_impl]
impl Widget for FocusableWidget {
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}
}

#[test]
fn derive_widget_focusable() {
    let w = FocusableWidget {
        own_id: Cell::new(None),
    };
    assert!(w.can_focus());
}

// ---------------------------------------------------------------------------
// Test 3: #[on(T)] event dispatch
// ---------------------------------------------------------------------------

/// A simple message type to test #[on(T)] dispatch.
struct PingMsg {
    value: u32,
}

#[derive(textual_rs::Widget)]
struct EventWidget {
    own_id: Cell<Option<WidgetId>>,
    received: Cell<u32>,
}

impl EventWidget {
    fn new() -> Self {
        Self {
            own_id: Cell::new(None),
            received: Cell::new(0),
        }
    }
}

#[textual_rs::widget_impl]
impl Widget for EventWidget {
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}

    #[on(PingMsg)]
    fn handle_ping(&self, msg: &PingMsg, _ctx: &AppContext) {
        self.received.set(msg.value);
    }
}

#[test]
fn on_event_dispatch() {
    let w = EventWidget::new();
    let ctx = AppContext::new();

    // Before dispatch: received is 0
    assert_eq!(w.received.get(), 0);

    // Dispatch a PingMsg — should be handled and return Stop
    let msg = PingMsg { value: 42 };
    let result = w.on_event(&msg as &dyn Any, &ctx);
    assert_eq!(result, EventPropagation::Stop);
    assert_eq!(w.received.get(), 42);

    // Dispatch an unrelated message — should return Continue
    let other: u64 = 99;
    let result2 = w.on_event(&other as &dyn Any, &ctx);
    assert_eq!(result2, EventPropagation::Continue);
    // received unchanged
    assert_eq!(w.received.get(), 42);
}

// ---------------------------------------------------------------------------
// Test 4: #[keybinding] dispatch
// ---------------------------------------------------------------------------

#[derive(textual_rs::Widget)]
#[focusable]
struct KeyWidget {
    own_id: Cell<Option<WidgetId>>,
    action_called: Cell<bool>,
}

impl KeyWidget {
    fn new() -> Self {
        Self {
            own_id: Cell::new(None),
            action_called: Cell::new(false),
        }
    }
}

#[textual_rs::widget_impl]
impl Widget for KeyWidget {
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}

    #[keybinding("enter", "submit")]
    fn handle_submit(&self, _ctx: &AppContext) {
        self.action_called.set(true);
    }
}

#[test]
fn keybinding_dispatch() {
    let w = KeyWidget::new();
    let ctx = AppContext::new();

    // Check that key_bindings returns at least one binding with action "submit"
    let bindings = w.key_bindings();
    assert!(!bindings.is_empty(), "Should have at least one key binding");
    assert!(
        bindings.iter().any(|b| b.action == "submit"),
        "Should have a 'submit' binding"
    );

    // Call on_action with the right action
    assert!(!w.action_called.get());
    w.on_action("submit", &ctx);
    assert!(
        w.action_called.get(),
        "on_action('submit') should have called handle_submit"
    );

    // Unknown action should be a no-op
    w.action_called.set(false);
    w.on_action("unknown_action", &ctx);
    assert!(!w.action_called.get());
}

// ---------------------------------------------------------------------------
// Test 5: Manual overrides not duplicated — user-provided methods take precedence
// ---------------------------------------------------------------------------

#[derive(textual_rs::Widget)]
struct ManualWidget {
    own_id: Cell<Option<WidgetId>>,
}

impl ManualWidget {
    fn new() -> Self {
        Self {
            own_id: Cell::new(None),
        }
    }
}

/// This widget manually provides can_focus — widget_impl must NOT override it.
#[textual_rs::widget_impl]
impl Widget for ManualWidget {
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}

    /// Manually override can_focus to return true, even though #[focusable] not set.
    fn can_focus(&self) -> bool {
        true
    }
}

#[test]
fn manual_override_not_duplicated() {
    let w = ManualWidget::new();
    // The manually provided can_focus should win
    assert!(w.can_focus());
    // widget_type_name should still come from the derive helper
    assert_eq!(w.widget_type_name(), "ManualWidget");
}

// ---------------------------------------------------------------------------
// Test 6: Multiple #[on(T)] annotations on the same widget
// ---------------------------------------------------------------------------

struct Msg1 {
    v: i32,
}
struct Msg2 {
    v: i32,
}

#[derive(textual_rs::Widget)]
struct MultiEventWidget {
    own_id: Cell<Option<WidgetId>>,
    msg1_count: Cell<i32>,
    msg2_count: Cell<i32>,
}

impl MultiEventWidget {
    fn new() -> Self {
        Self {
            own_id: Cell::new(None),
            msg1_count: Cell::new(0),
            msg2_count: Cell::new(0),
        }
    }
}

#[textual_rs::widget_impl]
impl Widget for MultiEventWidget {
    fn render(&self, _ctx: &AppContext, _area: Rect, _buf: &mut Buffer) {}

    #[on(Msg1)]
    fn handle_msg1(&self, msg: &Msg1, _ctx: &AppContext) {
        self.msg1_count.set(self.msg1_count.get() + msg.v);
    }

    #[on(Msg2)]
    fn handle_msg2(&self, msg: &Msg2, _ctx: &AppContext) {
        self.msg2_count.set(self.msg2_count.get() + msg.v);
    }
}

#[test]
fn multiple_on_annotations() {
    let w = MultiEventWidget::new();
    let ctx = AppContext::new();

    // Dispatch Msg1
    let m1 = Msg1 { v: 5 };
    let r1 = w.on_event(&m1 as &dyn Any, &ctx);
    assert_eq!(r1, EventPropagation::Stop);
    assert_eq!(w.msg1_count.get(), 5);
    assert_eq!(w.msg2_count.get(), 0);

    // Dispatch Msg2
    let m2 = Msg2 { v: 7 };
    let r2 = w.on_event(&m2 as &dyn Any, &ctx);
    assert_eq!(r2, EventPropagation::Stop);
    assert_eq!(w.msg1_count.get(), 5);
    assert_eq!(w.msg2_count.get(), 7);
}
