use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Region {
    WtWt,
    UsEn,
    UkEn,
    DeDe,
    FrFr,
    EsEs,
    JpJp,
    CnZh,
}

impl Region {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::WtWt => "wt-wt",
            Self::UsEn => "us-en",
            Self::UkEn => "uk-en",
            Self::DeDe => "de-de",
            Self::FrFr => "fr-fr",
            Self::EsEs => "es-es",
            Self::JpJp => "jp-jp",
            Self::CnZh => "cn-zh",
        }
    }
}

impl Default for Region {
    fn default() -> Self {
        Self::WtWt
    }
}

impl fmt::Display for Region {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SafeSearch {
    On,
    Moderate,
    Off,
}

impl SafeSearch {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::On => "1",
            Self::Moderate => "-1",
            Self::Off => "-2",
        }
    }
}

impl Default for SafeSearch {
    fn default() -> Self {
        Self::Moderate
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimeLimit {
    Day,
    Week,
    Month,
    Year,
}

impl TimeLimit {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Day => "d",
            Self::Week => "w",
            Self::Month => "m",
            Self::Year => "y",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextBackend {
    Auto,
    All,
    Api,
    Brave,
    Google,
    Grokipedia,
    Html,
    Lite,
    Mojeek,
    Startpage,
    Wikipedia,
    Yahoo,
    Yandex,
}

impl TextBackend {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Auto => "auto",
            Self::All => "all",
            Self::Api => "api",
            Self::Brave => "brave",
            Self::Google => "google",
            Self::Grokipedia => "grokipedia",
            Self::Html => "html",
            Self::Lite => "lite",
            Self::Mojeek => "mojeek",
            Self::Startpage => "startpage",
            Self::Wikipedia => "wikipedia",
            Self::Yahoo => "yahoo",
            Self::Yandex => "yandex",
        }
    }
}

impl Default for TextBackend {
    fn default() -> Self {
        Self::Auto
    }
}

#[derive(Debug, Clone)]
pub struct TextOptions {
    pub region: Region,
    pub safesearch: SafeSearch,
    pub timelimit: Option<TimeLimit>,
    pub max_results: Option<usize>,
    pub page: usize,
    pub backend: TextBackend,
}

impl Default for TextOptions {
    fn default() -> Self {
        Self {
            region: Region::default(),
            safesearch: SafeSearch::default(),
            timelimit: None,
            max_results: None,
            page: 1,
            backend: TextBackend::default(),
        }
    }
}

impl TextOptions {
    pub fn max_results(mut self, value: usize) -> Self {
        self.max_results = Some(value);
        self
    }

    pub fn region(mut self, value: Region) -> Self {
        self.region = value;
        self
    }

    pub fn safesearch(mut self, value: SafeSearch) -> Self {
        self.safesearch = value;
        self
    }

    pub fn timelimit(mut self, value: TimeLimit) -> Self {
        self.timelimit = Some(value);
        self
    }

    pub fn page(mut self, value: usize) -> Self {
        self.page = value;
        self
    }

    pub fn backend(mut self, value: TextBackend) -> Self {
        self.backend = value;
        self
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NewsBackend {
    Auto,
    All,
    Bing,
    Duckduckgo,
    Yahoo,
}

impl NewsBackend {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Auto => "auto",
            Self::All => "all",
            Self::Bing => "bing",
            Self::Duckduckgo => "duckduckgo",
            Self::Yahoo => "yahoo",
        }
    }
}

impl Default for NewsBackend {
    fn default() -> Self {
        Self::Auto
    }
}

#[derive(Debug, Clone)]
pub struct NewsOptions {
    pub region: Region,
    pub safesearch: SafeSearch,
    pub timelimit: Option<TimeLimit>,
    pub max_results: Option<usize>,
    pub page: usize,
    pub backend: NewsBackend,
}

impl Default for NewsOptions {
    fn default() -> Self {
        Self {
            region: Region::default(),
            safesearch: SafeSearch::default(),
            timelimit: None,
            max_results: None,
            page: 1,
            backend: NewsBackend::default(),
        }
    }
}

impl NewsOptions {
    pub fn max_results(mut self, value: usize) -> Self {
        self.max_results = Some(value);
        self
    }

    pub fn region(mut self, value: Region) -> Self {
        self.region = value;
        self
    }

    pub fn safesearch(mut self, value: SafeSearch) -> Self {
        self.safesearch = value;
        self
    }

    pub fn timelimit(mut self, value: TimeLimit) -> Self {
        self.timelimit = Some(value);
        self
    }

    pub fn page(mut self, value: usize) -> Self {
        self.page = value;
        self
    }

    pub fn backend(mut self, value: NewsBackend) -> Self {
        self.backend = value;
        self
    }
}

#[derive(Debug, Clone)]
pub struct BooksOptions {
    pub max_results: Option<usize>,
    pub page: usize,
}

impl Default for BooksOptions {
    fn default() -> Self {
        Self {
            max_results: None,
            page: 1,
        }
    }
}

impl BooksOptions {
    pub fn max_results(mut self, value: usize) -> Self {
        self.max_results = Some(value);
        self
    }

    pub fn page(mut self, value: usize) -> Self {
        self.page = value;
        self
    }
}
