use zeroize::Zeroize;

use crate::field_access;

/// Represents the [Entry](super::Entry) state.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
pub struct EntryState {
    visibility: EntryVisibility,
    mode: EntryMode,
    selection: EntrySelection,
    encryption: EntryEncryption,
}

impl EntryState {
    /// Creates a new [EntryState].
    pub const fn new() -> Self {
        Self {
            visibility: EntryVisibility::new(),
            mode: EntryMode::new(),
            selection: EntrySelection::new(),
            encryption: EntryEncryption::new(),
        }
    }

    /// Gets whether the [Entry] content is visible.
    pub const fn is_visible(&self) -> bool {
        matches!(self.visibility, EntryVisibility::Show)
    }

    /// Gets whether the [Entry] is in editing mode.
    pub const fn is_editing(&self) -> bool {
        matches!(self.mode, EntryMode::Editing)
    }

    /// Gets whether the [Entry] is selected.
    pub const fn is_selected(&self) -> bool {
        matches!(self.selection, EntrySelection::Selected)
    }

    /// Gets whether the [Entry] is encrypted.
    pub const fn is_encrypted(&self) -> bool {
        matches!(self.encryption, EntryEncryption::Encrypted)
    }
}

field_access! {
    EntryState {
        /// Visibility status of the entry.
        visibility: EntryVisibility,
        /// Editing mode of the entry.
        mode: EntryMode,
        /// Selection status of the entry.
        selection: EntrySelection,
        /// Encryption status of the entry.
        encryption: EntryEncryption,
    }
}

impl Default for EntryState {
    fn default() -> Self {
        Self::new()
    }
}

/// Represents the [Entry](super::Entry) visibility.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
pub enum EntryVisibility {
    /// Show the entry content.
    Show,
    /// Hide the entry content.
    Hide,
}

impl EntryVisibility {
    /// Creates a new [EntryVisibility].
    pub const fn new() -> Self {
        Self::Hide
    }
}

impl Default for EntryVisibility {
    fn default() -> Self {
        Self::new()
    }
}

/// Represents the [Entry](super::Entry) mode.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
pub enum EntryMode {
    /// Editing the entry.
    Editing,
    /// Viewing the entry.
    Viewing,
}

impl EntryMode {
    /// Creates a new [EntryMode].
    pub const fn new() -> Self {
        Self::Viewing
    }
}

impl Default for EntryMode {
    fn default() -> Self {
        Self::new()
    }
}

/// Represents the [Entry](super::Entry) selection status.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
pub enum EntrySelection {
    Selected,
    Unselected,
}

impl EntrySelection {
    /// Creates a new [EntrySelection].
    pub const fn new() -> Self {
        Self::Unselected
    }
}

impl Default for EntrySelection {
    fn default() -> Self {
        Self::new()
    }
}

/// Represents the [Entry](super::Entry) encryption status.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
pub enum EntryEncryption {
    Decrypted,
    Encrypted,
}

impl EntryEncryption {
    /// Creates a new [EntryEncryption].
    pub const fn new() -> Self {
        Self::Decrypted
    }
}

impl Default for EntryEncryption {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_entry_state() {
        [EntryVisibility::Show, EntryVisibility::Hide]
            .into_iter()
            .for_each(|visibility| {
                [EntryMode::Editing, EntryMode::Viewing]
                    .into_iter()
                    .for_each(|mode| {
                        [EntrySelection::Selected, EntrySelection::Unselected]
                            .into_iter()
                            .for_each(|selection| {
                                [EntryEncryption::Encrypted, EntryEncryption::Decrypted]
                                    .into_iter()
                                    .for_each(|encryption| {
                                        let entry_state = EntryState::new()
                                            .with_visibility(visibility)
                                            .with_mode(mode)
                                            .with_selection(selection)
                                            .with_encryption(encryption);

                                        assert_eq!(entry_state.visibility(), visibility);
                                        assert_eq!(
                                            entry_state.is_visible(),
                                            matches!(visibility, EntryVisibility::Show)
                                        );

                                        assert_eq!(entry_state.mode(), mode);
                                        assert_eq!(
                                            entry_state.is_editing(),
                                            matches!(mode, EntryMode::Editing)
                                        );

                                        assert_eq!(entry_state.selection(), selection);
                                        assert_eq!(
                                            entry_state.is_selected(),
                                            matches!(selection, EntrySelection::Selected)
                                        );

                                        assert_eq!(entry_state.encryption(), encryption);
                                        assert_eq!(
                                            entry_state.is_encrypted(),
                                            matches!(encryption, EntryEncryption::Encrypted)
                                        );
                                    });
                            });
                    });
            });
    }
}
