use crate::input::Modifiers;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MouseEvent {
    pub kind: MouseEventKind,
    pub column: u16,
    pub row: u16,
    pub modifiers: Modifiers,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseEventKind {
    Down(MouseButton),
    Up(MouseButton),
    Drag(MouseButton),
    Moved,
    ScrollUp,
    ScrollDown,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
}

impl TryFrom<crossterm::event::MouseButton> for MouseButton {
    type Error = ();

    fn try_from(b: crossterm::event::MouseButton) -> Result<Self, ()> {
        match b {
            crossterm::event::MouseButton::Left => Ok(MouseButton::Left),
            crossterm::event::MouseButton::Right => Ok(MouseButton::Right),
            crossterm::event::MouseButton::Middle => Ok(MouseButton::Middle),
        }
    }
}

impl TryFrom<crossterm::event::MouseEventKind> for MouseEventKind {
    type Error = ();

    fn try_from(k: crossterm::event::MouseEventKind) -> Result<Self, ()> {
        use crossterm::event::MouseEventKind as Ck;
        match k {
            Ck::Down(b) => Ok(MouseEventKind::Down(b.try_into()?)),
            Ck::Up(b) => Ok(MouseEventKind::Up(b.try_into()?)),
            Ck::Drag(b) => Ok(MouseEventKind::Drag(b.try_into()?)),
            Ck::Moved => Ok(MouseEventKind::Moved),
            Ck::ScrollUp => Ok(MouseEventKind::ScrollUp),
            Ck::ScrollDown => Ok(MouseEventKind::ScrollDown),
            _ => Err(()),
        }
    }
}

impl TryFrom<crossterm::event::MouseEvent> for MouseEvent {
    type Error = ();

    fn try_from(m: crossterm::event::MouseEvent) -> Result<Self, ()> {
        Ok(MouseEvent {
            kind: m.kind.try_into()?,
            column: m.column,
            row: m.row,
            modifiers: m.modifiers.into(),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crossterm::event as ct;

    // --- MouseButton ---
    #[test]
    fn button_left() {
        assert_eq!(
            MouseButton::try_from(ct::MouseButton::Left),
            Ok(MouseButton::Left)
        );
    }

    #[test]
    fn button_right() {
        assert_eq!(
            MouseButton::try_from(ct::MouseButton::Right),
            Ok(MouseButton::Right)
        );
    }

    #[test]
    fn button_middle() {
        assert_eq!(
            MouseButton::try_from(ct::MouseButton::Middle),
            Ok(MouseButton::Middle)
        );
    }

    // --- MouseEventKind ---
    #[test]
    fn kind_down_left() {
        assert_eq!(
            MouseEventKind::try_from(ct::MouseEventKind::Down(ct::MouseButton::Left)),
            Ok(MouseEventKind::Down(MouseButton::Left))
        );
    }

    #[test]
    fn kind_up_right() {
        assert_eq!(
            MouseEventKind::try_from(ct::MouseEventKind::Up(ct::MouseButton::Right)),
            Ok(MouseEventKind::Up(MouseButton::Right))
        );
    }

    #[test]
    fn kind_drag_middle() {
        assert_eq!(
            MouseEventKind::try_from(ct::MouseEventKind::Drag(ct::MouseButton::Middle)),
            Ok(MouseEventKind::Drag(MouseButton::Middle))
        );
    }

    #[test]
    fn kind_moved() {
        assert_eq!(
            MouseEventKind::try_from(ct::MouseEventKind::Moved),
            Ok(MouseEventKind::Moved)
        );
    }

    #[test]
    fn kind_scroll_up() {
        assert_eq!(
            MouseEventKind::try_from(ct::MouseEventKind::ScrollUp),
            Ok(MouseEventKind::ScrollUp)
        );
    }

    #[test]
    fn kind_scroll_down() {
        assert_eq!(
            MouseEventKind::try_from(ct::MouseEventKind::ScrollDown),
            Ok(MouseEventKind::ScrollDown)
        );
    }

    // Fallback arm — ScrollLeft is present in crossterm ≥0.27
    #[test]
    fn kind_scroll_left_is_err() {
        assert!(
            MouseEventKind::try_from(ct::MouseEventKind::ScrollLeft).is_err()
        );
    }

    // --- MouseEvent (full round-trip) ---
    fn make_ct_event(
        kind: ct::MouseEventKind,
        col: u16,
        row: u16,
        mods: ct::KeyModifiers,
    ) -> ct::MouseEvent {
        ct::MouseEvent {
            kind,
            column: col,
            row,
            modifiers: mods,
        }
    }

    #[test]
    fn mouse_event_success() {
        let ev = MouseEvent::try_from(make_ct_event(
            ct::MouseEventKind::Down(ct::MouseButton::Left),
            10,
            5,
            ct::KeyModifiers::empty(),
        ))
        .unwrap();
        assert_eq!(ev.kind, MouseEventKind::Down(MouseButton::Left));
        assert_eq!(ev.column, 10);
        assert_eq!(ev.row, 5);
        assert!(ev.modifiers.is_empty());
    }

    #[test]
    fn mouse_event_with_ctrl_modifier() {
        let ev = MouseEvent::try_from(make_ct_event(
            ct::MouseEventKind::ScrollUp,
            0,
            0,
            ct::KeyModifiers::CONTROL,
        ))
        .unwrap();
        assert!(ev.modifiers.contains(Modifiers::CTRL));
    }

    #[test]
    fn mouse_event_unknown_kind_is_err() {
        assert!(MouseEvent::try_from(make_ct_event(
            ct::MouseEventKind::ScrollLeft,
            0,
            0,
            ct::KeyModifiers::empty(),
        ))
        .is_err());
    }

    // --- Derived trait smoke tests ---
    #[test]
    fn mouse_event_clone_eq() {
        let ev = MouseEvent {
            kind: MouseEventKind::Moved,
            column: 1,
            row: 2,
            modifiers: Modifiers::empty(),
        };
        assert_eq!(ev.clone(), ev);
    }
}
