use std::fmt;

use bitflags::bitflags;

bitflags! {
    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct Modifiers: u8 {
        const CTRL  = 0b0001;
        const ALT   = 0b0010;
        const SHIFT = 0b0100;
    }
}

use crossterm::event::KeyModifiers;

impl From<KeyModifiers> for Modifiers {
    fn from(value: KeyModifiers) -> Self {
        let mut mods = Modifiers::empty();

        if value.contains(KeyModifiers::CONTROL) {
            mods |= Modifiers::CTRL;
        }

        if value.contains(KeyModifiers::ALT) {
            mods |= Modifiers::ALT;
        }

        if value.contains(KeyModifiers::SHIFT) {
            mods |= Modifiers::SHIFT;
        }

        mods
    }
}

impl fmt::Display for Modifiers {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut first = true;

        if self.contains(Modifiers::CTRL) {
            write!(f, "Ctrl")?;
            first = false;
        }

        if self.contains(Modifiers::ALT) {
            if !first {
                write!(f, "+")?;
            }
            write!(f, "Alt")?;
            first = false;
        }

        if self.contains(Modifiers::SHIFT) {
            if !first {
                write!(f, "+")?;
            }
            write!(f, "Shift")?;
        }

        Ok(())
    }
}

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

    // --- From<KeyModifiers> ---
    #[test]
    fn from_empty() {
        assert!(Modifiers::from(KeyModifiers::empty()).is_empty());
    }

    #[test]
    fn from_ctrl() {
        assert!(Modifiers::from(KeyModifiers::CONTROL).contains(Modifiers::CTRL));
    }

    #[test]
    fn from_alt() {
        assert!(Modifiers::from(KeyModifiers::ALT).contains(Modifiers::ALT));
    }

    #[test]
    fn from_shift() {
        assert!(Modifiers::from(KeyModifiers::SHIFT).contains(Modifiers::SHIFT));
    }

    #[test]
    fn from_ctrl_does_not_set_alt_or_shift() {
        let m = Modifiers::from(KeyModifiers::CONTROL);
        assert!(!m.contains(Modifiers::ALT));
        assert!(!m.contains(Modifiers::SHIFT));
    }

    #[test]
    fn from_ctrl_alt() {
        let m = Modifiers::from(KeyModifiers::CONTROL | KeyModifiers::ALT);
        assert!(m.contains(Modifiers::CTRL));
        assert!(m.contains(Modifiers::ALT));
        assert!(!m.contains(Modifiers::SHIFT));
    }

    #[test]
    fn from_ctrl_shift() {
        let m = Modifiers::from(KeyModifiers::CONTROL | KeyModifiers::SHIFT);
        assert!(m.contains(Modifiers::CTRL));
        assert!(!m.contains(Modifiers::ALT));
        assert!(m.contains(Modifiers::SHIFT));
    }

    #[test]
    fn from_alt_shift() {
        let m = Modifiers::from(KeyModifiers::ALT | KeyModifiers::SHIFT);
        assert!(!m.contains(Modifiers::CTRL));
        assert!(m.contains(Modifiers::ALT));
        assert!(m.contains(Modifiers::SHIFT));
    }

    #[test]
    fn from_all() {
        let m = Modifiers::from(KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT);
        assert!(m.contains(Modifiers::CTRL));
        assert!(m.contains(Modifiers::ALT));
        assert!(m.contains(Modifiers::SHIFT));
    }

    // --- Display ---
    #[test]
    fn display_empty() {
        assert_eq!(Modifiers::empty().to_string(), "");
    }

    #[test]
    fn display_ctrl() {
        assert_eq!(Modifiers::CTRL.to_string(), "Ctrl");
    }

    #[test]
    fn display_alt() {
        assert_eq!(Modifiers::ALT.to_string(), "Alt");
    }

    #[test]
    fn display_shift() {
        assert_eq!(Modifiers::SHIFT.to_string(), "Shift");
    }

    #[test]
    fn display_ctrl_alt() {
        assert_eq!((Modifiers::CTRL | Modifiers::ALT).to_string(), "Ctrl+Alt");
    }

    #[test]
    fn display_ctrl_shift() {
        assert_eq!(
            (Modifiers::CTRL | Modifiers::SHIFT).to_string(),
            "Ctrl+Shift"
        );
    }

    #[test]
    fn display_alt_shift() {
        assert_eq!(
            (Modifiers::ALT | Modifiers::SHIFT).to_string(),
            "Alt+Shift"
        );
    }

    #[test]
    fn display_all() {
        assert_eq!(
            (Modifiers::CTRL | Modifiers::ALT | Modifiers::SHIFT).to_string(),
            "Ctrl+Alt+Shift"
        );
    }

    // --- Default ---
    #[test]
    fn default_is_empty() {
        assert!(Modifiers::default().is_empty());
    }
}
