use std::str::FromStr;
use std::string::ParseError;

use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, PartialOrd, Serialize)]
#[serde(from = "&str")]
#[repr(u8)]
pub enum TagSearchMode {
    /// All (AND).
    All = 0,
    /// Any (OR).
    Any = 1,
}

impl Default for TagSearchMode {
    fn default() -> Self {
        0u8.into()
    }
}

impl std::fmt::Display for TagSearchMode {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str(match self {
            Self::All => "all",
            Self::Any => "any",
        })
    }
}

impl From<&str> for TagSearchMode {
    fn from(value: &str) -> Self {
        match value {
            "all" => Self::All,
            "any" => Self::Any,
            _ => Self::All,
        }
    }
}

impl FromStr for TagSearchMode {
    type Err = ParseError;

    fn from_str(value: &str) -> Result<Self, ParseError> {
        Ok(match value {
            "all" => Self::All,
            "any" => Self::Any,
            _ => Self::All,
        })
    }
}

impl From<u8> for TagSearchMode {
    fn from(value: u8) -> Self {
        match value {
            0 => Self::All,
            1 => Self::Any,
            _ => Self::All,
        }
    }
}

impl From<u16> for TagSearchMode {
    fn from(value: u16) -> Self {
        match value {
            0 => Self::All,
            1 => Self::Any,
            _ => Self::All,
        }
    }
}

impl From<u32> for TagSearchMode {
    fn from(value: u32) -> Self {
        match value {
            0 => Self::All,
            1 => Self::Any,
            _ => Self::All,
        }
    }
}

impl From<u64> for TagSearchMode {
    fn from(value: u64) -> Self {
        match value {
            0 => Self::All,
            1 => Self::Any,
            _ => Self::All,
        }
    }
}

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

    #[test]
    fn tag_search_mode_produces_all_from_default() {
        let tag_search_mode = TagSearchMode::default();
        assert_eq!(tag_search_mode, TagSearchMode::All);
    }

    #[test]
    fn tag_search_mode_produces_any_from_1() {
        let tag_search_mode = TagSearchMode::from(1u8);
        assert_eq!(tag_search_mode, TagSearchMode::Any);
    }

    #[test]
    fn tag_search_mode_produces_all_from_21() {
        let tag_search_mode = TagSearchMode::from(21u8);
        assert_eq!(tag_search_mode, TagSearchMode::All);
    }
}
