#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
    Char(char),
    ArrowUp,
    ArrowDown,
    ArrowLeft,
    ArrowRight,
    F(u8),
    Enter,
    Escape,
    Tab,
    BackTab,
    Backspace,
    Delete,
    Home,
    End,
    PageUp,
    PageDown,
}

use crossterm::event::KeyCode;

impl TryFrom<KeyCode> for Key {
    type Error = ();

    fn try_from(code: KeyCode) -> Result<Self, Self::Error> {
        Ok(match code {
            KeyCode::Char(c) => Key::Char(c),

            KeyCode::Up => Key::ArrowUp,
            KeyCode::Down => Key::ArrowDown,
            KeyCode::Left => Key::ArrowLeft,
            KeyCode::Right => Key::ArrowRight,

            KeyCode::Enter => Key::Enter,
            KeyCode::Esc => Key::Escape,
            KeyCode::Tab => Key::Tab,
            KeyCode::BackTab => Key::BackTab,
            KeyCode::Backspace => Key::Backspace,
            KeyCode::Delete => Key::Delete,
            KeyCode::Home => Key::Home,
            KeyCode::End => Key::End,
            KeyCode::PageUp => Key::PageUp,
            KeyCode::PageDown => Key::PageDown,

            KeyCode::F(n) => Key::F(n),

            _ => return Err(()),
        })
    }
}
use std::fmt;

impl fmt::Display for Key {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Key::Char(c) => write!(f, "{}", c.to_ascii_lowercase()),
            Key::ArrowUp => write!(f, "ArrowUp"),
            Key::ArrowDown => write!(f, "ArrowDown"),
            Key::ArrowLeft => write!(f, "ArrowLeft"),
            Key::ArrowRight => write!(f, "ArrowRight"),
            Key::F(n) => write!(f, "F{}", n),
            Key::Enter => write!(f, "Enter"),
            Key::Escape => write!(f, "Escape"),
            Key::Tab => write!(f, "Tab"),
            Key::BackTab => write!(f, "BackTab"),
            Key::Backspace => write!(f, "Backspace"),
            Key::Delete => write!(f, "Delete"),
            Key::Home => write!(f, "Home"),
            Key::End => write!(f, "End"),
            Key::PageUp => write!(f, "PageUp"),
            Key::PageDown => write!(f, "PageDown"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    // --- TryFrom<KeyCode> ---
    #[test]
    fn try_from_char() {
        assert_eq!(Key::try_from(KeyCode::Char('a')), Ok(Key::Char('a')));
    }

    #[test]
    fn try_from_up() {
        assert_eq!(Key::try_from(KeyCode::Up), Ok(Key::ArrowUp));
    }

    #[test]
    fn try_from_down() {
        assert_eq!(Key::try_from(KeyCode::Down), Ok(Key::ArrowDown));
    }

    #[test]
    fn try_from_left() {
        assert_eq!(Key::try_from(KeyCode::Left), Ok(Key::ArrowLeft));
    }

    #[test]
    fn try_from_right() {
        assert_eq!(Key::try_from(KeyCode::Right), Ok(Key::ArrowRight));
    }

    #[test]
    fn try_from_enter() {
        assert_eq!(Key::try_from(KeyCode::Enter), Ok(Key::Enter));
    }

    #[test]
    fn try_from_escape() {
        assert_eq!(Key::try_from(KeyCode::Esc), Ok(Key::Escape));
    }

    #[test]
    fn try_from_tab() {
        assert_eq!(Key::try_from(KeyCode::Tab), Ok(Key::Tab));
    }

    #[test]
    fn try_from_backtab() {
        assert_eq!(Key::try_from(KeyCode::BackTab), Ok(Key::BackTab));
    }

    #[test]
    fn try_from_backspace() {
        assert_eq!(Key::try_from(KeyCode::Backspace), Ok(Key::Backspace));
    }

    #[test]
    fn try_from_delete() {
        assert_eq!(Key::try_from(KeyCode::Delete), Ok(Key::Delete));
    }

    #[test]
    fn try_from_home() {
        assert_eq!(Key::try_from(KeyCode::Home), Ok(Key::Home));
    }

    #[test]
    fn try_from_end() {
        assert_eq!(Key::try_from(KeyCode::End), Ok(Key::End));
    }

    #[test]
    fn try_from_page_up() {
        assert_eq!(Key::try_from(KeyCode::PageUp), Ok(Key::PageUp));
    }

    #[test]
    fn try_from_page_down() {
        assert_eq!(Key::try_from(KeyCode::PageDown), Ok(Key::PageDown));
    }

    #[test]
    fn try_from_f_key() {
        assert_eq!(Key::try_from(KeyCode::F(1)), Ok(Key::F(1)));
    }

    #[test]
    fn try_from_f_key_12() {
        assert_eq!(Key::try_from(KeyCode::F(12)), Ok(Key::F(12)));
    }

    #[test]
    fn try_from_null_is_err() {
        assert!(Key::try_from(KeyCode::Null).is_err());
    }

    // --- Display ---
    #[test]
    fn display_char_uppercase_lowercased() {
        assert_eq!(Key::Char('A').to_string(), "a");
    }

    #[test]
    fn display_char_already_lower() {
        assert_eq!(Key::Char('z').to_string(), "z");
    }

    #[test]
    fn display_char_digit() {
        assert_eq!(Key::Char('5').to_string(), "5");
    }

    #[test]
    fn display_arrow_up() {
        assert_eq!(Key::ArrowUp.to_string(), "ArrowUp");
    }

    #[test]
    fn display_arrow_down() {
        assert_eq!(Key::ArrowDown.to_string(), "ArrowDown");
    }

    #[test]
    fn display_arrow_left() {
        assert_eq!(Key::ArrowLeft.to_string(), "ArrowLeft");
    }

    #[test]
    fn display_arrow_right() {
        assert_eq!(Key::ArrowRight.to_string(), "ArrowRight");
    }

    #[test]
    fn display_enter() {
        assert_eq!(Key::Enter.to_string(), "Enter");
    }

    #[test]
    fn display_escape() {
        assert_eq!(Key::Escape.to_string(), "Escape");
    }

    #[test]
    fn display_tab() {
        assert_eq!(Key::Tab.to_string(), "Tab");
    }

    #[test]
    fn display_backtab() {
        assert_eq!(Key::BackTab.to_string(), "BackTab");
    }

    #[test]
    fn display_backspace() {
        assert_eq!(Key::Backspace.to_string(), "Backspace");
    }

    #[test]
    fn display_delete() {
        assert_eq!(Key::Delete.to_string(), "Delete");
    }

    #[test]
    fn display_home() {
        assert_eq!(Key::Home.to_string(), "Home");
    }

    #[test]
    fn display_end() {
        assert_eq!(Key::End.to_string(), "End");
    }

    #[test]
    fn display_page_up() {
        assert_eq!(Key::PageUp.to_string(), "PageUp");
    }

    #[test]
    fn display_page_down() {
        assert_eq!(Key::PageDown.to_string(), "PageDown");
    }

    #[test]
    fn display_f_key() {
        assert_eq!(Key::F(12).to_string(), "F12");
    }

    // --- Derived trait smoke tests ---
    #[test]
    fn key_clone_eq() {
        assert_eq!(Key::Enter.clone(), Key::Enter);
    }

    #[test]
    fn key_ne() {
        assert_ne!(Key::Enter, Key::Escape);
    }

    #[test]
    fn key_hash() {
        use std::collections::HashSet;
        let mut s = HashSet::new();
        s.insert(Key::Char('a'));
        assert!(s.contains(&Key::Char('a')));
        assert!(!s.contains(&Key::Char('b')));
    }
}
