use chrono::{DateTime, Utc};
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod};
use oauth2_types::scope::Scope;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use ulid::Ulid;
use url::Url;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum DiscoveryMode {
    #[default]
    Oidc,
    Insecure,
    Disabled,
}
impl DiscoveryMode {
    #[must_use]
    pub fn is_disabled(&self) -> bool {
        matches!(self, DiscoveryMode::Disabled)
    }
}
#[derive(Debug, Clone, Error)]
#[error("Invalid discovery mode {0:?}")]
pub struct InvalidDiscoveryModeError(String);
impl std::str::FromStr for DiscoveryMode {
    type Err = InvalidDiscoveryModeError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "oidc" => Ok(Self::Oidc),
            "insecure" => Ok(Self::Insecure),
            "disabled" => Ok(Self::Disabled),
            s => Err(InvalidDiscoveryModeError(s.to_owned())),
        }
    }
}
impl DiscoveryMode {
    #[must_use]
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Oidc => "oidc",
            Self::Insecure => "insecure",
            Self::Disabled => "disabled",
        }
    }
}
impl std::fmt::Display for DiscoveryMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum PkceMode {
    #[default]
    Auto,
    S256,
    Disabled,
}
#[derive(Debug, Clone, Error)]
#[error("Invalid PKCE mode {0:?}")]
pub struct InvalidPkceModeError(String);
impl std::str::FromStr for PkceMode {
    type Err = InvalidPkceModeError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "auto" => Ok(Self::Auto),
            "s256" => Ok(Self::S256),
            "disabled" => Ok(Self::Disabled),
            s => Err(InvalidPkceModeError(s.to_owned())),
        }
    }
}
impl PkceMode {
    #[must_use]
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Auto => "auto",
            Self::S256 => "s256",
            Self::Disabled => "disabled",
        }
    }
}
impl std::fmt::Display for PkceMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct UpstreamOAuthProvider {
    pub id: Ulid,
    pub issuer: String,
    pub human_name: Option<String>,
    pub brand_name: Option<String>,
    pub discovery_mode: DiscoveryMode,
    pub pkce_mode: PkceMode,
    pub jwks_uri_override: Option<Url>,
    pub authorization_endpoint_override: Option<Url>,
    pub token_endpoint_override: Option<Url>,
    pub scope: Scope,
    pub client_id: String,
    pub encrypted_client_secret: Option<String>,
    pub token_endpoint_signing_alg: Option<JsonWebSignatureAlg>,
    pub token_endpoint_auth_method: OAuthClientAuthenticationMethod,
    pub created_at: DateTime<Utc>,
    pub disabled_at: Option<DateTime<Utc>>,
    pub claims_imports: ClaimsImports,
    pub additional_authorization_parameters: Vec<(String, String)>,
}
impl PartialOrd for UpstreamOAuthProvider {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.id.cmp(&other.id))
    }
}
impl Ord for UpstreamOAuthProvider {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.id.cmp(&other.id)
    }
}
impl UpstreamOAuthProvider {
    #[must_use]
    pub const fn enabled(&self) -> bool {
        self.disabled_at.is_none()
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum SetEmailVerification {
    Always,
    Never,
    #[default]
    Import,
}
impl SetEmailVerification {
    #[must_use]
    pub fn should_mark_as_verified(&self, upstream_verified: bool) -> bool {
        match self {
            Self::Always => true,
            Self::Never => false,
            Self::Import => upstream_verified,
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct ClaimsImports {
    #[serde(default)]
    pub subject: SubjectPreference,
    #[serde(default)]
    pub localpart: ImportPreference,
    #[serde(default)]
    pub displayname: ImportPreference,
    #[serde(default)]
    pub email: ImportPreference,
    #[serde(default)]
    pub verify_email: SetEmailVerification,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct SubjectPreference {
    #[serde(default)]
    pub template: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct ImportPreference {
    #[serde(default)]
    pub action: ImportAction,
    #[serde(default)]
    pub template: Option<String>,
}
impl std::ops::Deref for ImportPreference {
    type Target = ImportAction;
    fn deref(&self) -> &Self::Target {
        &self.action
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum ImportAction {
    #[default]
    Ignore,
    Suggest,
    Force,
    Require,
}
impl ImportAction {
    #[must_use]
    pub fn is_forced(&self) -> bool {
        matches!(self, Self::Force | Self::Require)
    }
    #[must_use]
    pub fn ignore(&self) -> bool {
        matches!(self, Self::Ignore)
    }
    #[must_use]
    pub fn is_required(&self) -> bool {
        matches!(self, Self::Require)
    }
    #[must_use]
    pub fn should_import(&self, user_preference: bool) -> bool {
        match self {
            Self::Ignore => false,
            Self::Suggest => user_preference,
            Self::Force | Self::Require => true,
        }
    }
}