use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use thiserror::Error;

use crate::admin_roles::RoleAssignment;
use crate::credentials::CredentialRecord;
use crate::credentials::MtlsClientCertificate;
use crate::identity::{CustomIdentity, Principal, Tenant};
use crate::providers::IdentityProvider;
use crate::sts::CredentialScope;
use bucketwarden_s3::sigv4::AwsCredentials;

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct AuthStore {
    #[serde(default)]
    pub(crate) tenants: BTreeMap<String, Tenant>,
    pub(crate) principals: BTreeMap<String, Principal>,
    pub(crate) credentials: BTreeMap<String, CredentialRecord>,
    #[serde(default)]
    pub(crate) mtls_certificates: BTreeMap<String, MtlsClientCertificate>,
    #[serde(default)]
    pub(crate) role_assignments: BTreeMap<String, Vec<RoleAssignment>>,
    #[serde(default)]
    pub(crate) identity_providers: BTreeMap<String, IdentityProvider>,
    #[serde(default)]
    pub(crate) custom_identities: BTreeMap<String, CustomIdentity>,
    #[serde(default)]
    pub(crate) login_attempts: BTreeMap<String, LoginAttemptRecord>,
}

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct LoginAttemptRecord {
    pub principal_id: String,
    pub consecutive_failed_attempts: u32,
    pub last_failed_epoch_seconds: Option<u64>,
    pub last_success_epoch_seconds: Option<u64>,
    pub last_failure_detail: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResolvedCredential {
    pub principal_id: String,
    pub tenant_id: String,
    pub access_key_id: String,
    pub credentials: AwsCredentials,
    pub scope: Option<CredentialScope>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResolvedMtlsIdentity {
    pub principal_id: String,
    pub tenant_id: String,
    pub certificate_fingerprint: String,
}

#[derive(Clone, Debug, Error, Eq, PartialEq)]
pub enum AuthError {
    #[error("unknown tenant: {0}")]
    UnknownTenant(String),
    #[error("disabled tenant: {0}")]
    DisabledTenant(String),
    #[error("unknown principal: {0}")]
    UnknownPrincipal(String),
    #[error("disabled principal: {0}")]
    DisabledPrincipal(String),
    #[error("unknown access key: {0}")]
    UnknownAccessKey(String),
    #[error("unknown parent access key: {0}")]
    UnknownParentAccessKey(String),
    #[error("disabled access key: {0}")]
    DisabledAccessKey(String),
    #[error("unknown client certificate: {0}")]
    UnknownClientCertificate(String),
    #[error("disabled client certificate: {0}")]
    DisabledClientCertificate(String),
    #[error("revoked credential: {0}")]
    RevokedCredential(String),
    #[error("expired credential: {0}")]
    ExpiredCredential(String),
    #[error("not an access key: {0}")]
    NotAccessKey(String),
    #[error("unknown identity provider: {0}")]
    UnknownIdentityProvider(String),
    #[error("invalid web identity token: {0}")]
    InvalidWebIdentityToken(String),
    #[error("web identity token issuer mismatch: {0}")]
    WebIdentityIssuerMismatch(String),
    #[error("web identity token audience mismatch: {0}")]
    WebIdentityAudienceMismatch(String),
    #[error("web identity token expired")]
    WebIdentityTokenExpired,
    #[error("unsupported web identity token algorithm: {0}")]
    UnsupportedWebIdentityAlgorithm(String),
    #[error("web identity key id mismatch: {0}")]
    WebIdentityKeyIdMismatch(String),
    #[error("invalid identity provider token: {0}")]
    InvalidIdentityProviderToken(String),
    #[error("unknown directory subject: {0}")]
    UnknownDirectorySubject(String),
    #[error("disabled directory subject: {0}")]
    DisabledDirectorySubject(String),
    #[error("unsupported identity provider kind: {0}")]
    UnsupportedIdentityProviderKind(String),
    #[error(
        "role assumption denied for principal {principal_id} on {role_resource} as {role_name}"
    )]
    RoleAssumptionDenied {
        principal_id: String,
        role_name: String,
        role_resource: String,
    },
    #[error("unknown custom identity: {0}")]
    UnknownCustomIdentity(String),
    #[error("disabled custom identity: {0}")]
    DisabledCustomIdentity(String),
    #[error("invalid custom identity secret: {0}")]
    InvalidCustomIdentitySecret(String),
    #[error("too many failed login attempts for principal: {0}")]
    TooManyLoginAttempts(String),
}
