use kittybox_indieauth::{AuthorizationRequest, TokenData};
pub use kittybox_util::auth::EnrolledCredential;
use std::collections::HashMap;
use std::future::Future;

type Result<T> = std::io::Result<T>;

pub mod fs;
pub use fs::FileBackend;

pub trait AuthBackend: Clone + Send + Sync + 'static {
    /// Initialize self from URL, possibly performing initialization.
    fn new(url: &'_ url::Url) -> impl Future<Output = Result<Self>> + Send;
    // Authorization code management.
    /// Create a one-time OAuth2 authorization code for the passed
    /// authorization request, and save it for later retrieval.
    ///
    /// Note for implementors: the [`AuthorizationRequest::me`] value
    /// is guaranteed to be [`Some(url::Url)`][Option::Some] and can
    /// be trusted to be correct and non-malicious.
    fn create_code(
        &self,
        data: AuthorizationRequest,
    ) -> impl Future<Output = Result<String>> + Send;
    /// Retreive an authorization request using the one-time
    /// code. Implementations must sanitize the `code` field to
    /// prevent exploits, and must check if the code should still be
    /// valid at this point in time (validity interval is left up to
    /// the implementation, but is recommended to be no more than 10
    /// minutes).
    fn get_code(
        &self,
        code: &str,
    ) -> impl Future<Output = Result<Option<AuthorizationRequest>>> + Send;
    // Token management.
    fn create_token(&self, data: TokenData) -> impl Future<Output = Result<String>> + Send;
    fn get_token(
        &self,
        website: &url::Url,
        token: &str,
    ) -> impl Future<Output = Result<Option<TokenData>>> + Send;
    fn list_tokens(
        &self,
        website: &url::Url,
    ) -> impl Future<Output = Result<HashMap<String, TokenData>>> + Send;
    fn revoke_token(
        &self,
        website: &url::Url,
        token: &str,
    ) -> impl Future<Output = Result<()>> + Send;
    // Refresh token management.
    fn create_refresh_token(&self, data: TokenData) -> impl Future<Output = Result<String>> + Send;
    fn get_refresh_token(
        &self,
        website: &url::Url,
        token: &str,
    ) -> impl Future<Output = Result<Option<TokenData>>> + Send;
    fn list_refresh_tokens(
        &self,
        website: &url::Url,
    ) -> impl Future<Output = Result<HashMap<String, TokenData>>> + Send;
    fn revoke_refresh_token(
        &self,
        website: &url::Url,
        token: &str,
    ) -> impl Future<Output = Result<()>> + Send;
    // Password management.
    /// Verify a password.
    #[must_use]
    fn verify_password(
        &self,
        website: &url::Url,
        password: String,
    ) -> impl Future<Output = Result<bool>> + Send;
    /// Enroll a password credential for a user. Only one password
    /// credential must exist for a given user.
    fn enroll_password(
        &self,
        website: &url::Url,
        password: String,
    ) -> impl Future<Output = Result<()>> + Send;
    /// List currently enrolled credential types for a given user.
    fn list_user_credential_types(
        &self,
        website: &url::Url,
    ) -> impl Future<Output = Result<Vec<EnrolledCredential>>> + Send;
    // WebAuthn credential management.
    #[cfg(feature = "webauthn")]
    /// Enroll a WebAuthn authenticator public key for this user.
    /// Multiple public keys may be saved for one user, corresponding
    /// to different authenticators used by them.
    ///
    /// This function can also be used to overwrite a passkey with an
    /// updated version after using
    /// [webauthn::prelude::Passkey::update_credential()].
    fn enroll_webauthn(
        &self,
        website: &url::Url,
        credential: webauthn::prelude::Passkey,
    ) -> impl Future<Output = Result<()>> + Send;
    #[cfg(feature = "webauthn")]
    /// List currently enrolled WebAuthn authenticators for a given user.
    fn list_webauthn_pubkeys(
        &self,
        website: &url::Url,
    ) -> impl Future<Output = Result<Vec<webauthn::prelude::Passkey>>> + Send;
    #[cfg(feature = "webauthn")]
    /// Persist registration challenge state for a little while so it
    /// can be used later.
    ///
    /// Challenges saved in this manner MUST expire after a little
    /// while. 10 minutes is recommended.
    fn persist_registration_challenge(
        &self,
        website: &url::Url,
        state: webauthn::prelude::PasskeyRegistration,
    ) -> impl Future<Output = Result<String>> + Send;
    #[cfg(feature = "webauthn")]
    /// Retrieve a persisted registration challenge.
    ///
    /// The challenge should be deleted after retrieval.
    fn retrieve_registration_challenge(
        &self,
        website: &url::Url,
        challenge_id: &str,
    ) -> impl Future<Output = Result<webauthn::prelude::PasskeyRegistration>> + Send;
    #[cfg(feature = "webauthn")]
    /// Persist authentication challenge state for a little while so
    /// it can be used later.
    ///
    /// Challenges saved in this manner MUST expire after a little
    /// while. 10 minutes is recommended.
    ///
    /// To support multiple authentication options, this can return an
    /// opaque token that should be set as a cookie.
    fn persist_authentication_challenge(
        &self,
        website: &url::Url,
        state: webauthn::prelude::PasskeyAuthentication,
    ) -> impl Future<Output = Result<String>> + Send;
    #[cfg(feature = "webauthn")]
    /// Retrieve a persisted authentication challenge.
    ///
    /// The challenge should be deleted after retrieval.
    fn retrieve_authentication_challenge(
        &self,
        website: &url::Url,
        challenge_id: &str,
    ) -> impl Future<Output = Result<webauthn::prelude::PasskeyAuthentication>> + Send;
}