#![doc = include_str!("../README.md")]
mod atproto;
mod constants;
mod error;
mod http_client;
mod jose;
mod keyset;
mod oauth_client;
mod oauth_session;
mod resolver;
mod server_agent;
pub mod store;
mod types;
mod utils;

pub use atproto::{
    AtprotoClientMetadata, AtprotoLocalhostClientMetadata, AuthMethod, GrantType, KnownScope, Scope,
};
pub use error::{Error, Result};
#[cfg(feature = "default-client")]
pub use http_client::default::DefaultHttpClient;
pub use http_client::dpop::DpopClient;
pub use oauth_client::{OAuthClient, OAuthClientConfig};
pub use oauth_session::OAuthSession;
pub use resolver::OAuthResolverConfig;
pub use types::{
    AuthorizeOptionPrompt, AuthorizeOptions, CallbackParams, OAuthClientMetadata, TokenSet,
};

#[cfg(test)]
mod tests {
    use crate::{
        AtprotoLocalhostClientMetadata, OAuthResolverConfig,
        resolver::OAuthResolver,
        types::{
            OAuthAuthorizationServerMetadata, OAuthClientMetadata, OAuthProtectedResourceMetadata,
            TryIntoOAuthClientMetadata,
        },
    };
    use atrium_api::{
        did_doc::{DidDocument, Service},
        types::string::{Did, Handle},
    };
    use atrium_common::resolver::Resolver;
    use atrium_xrpc::HttpClient;
    use jose_jwk::Key;
    use std::sync::Arc;

    pub struct MockDidResolver;

    impl Resolver for MockDidResolver {
        type Input = Did;
        type Output = DidDocument;
        type Error = atrium_identity::Error;
        async fn resolve(&self, did: &Self::Input) -> Result<Self::Output, Self::Error> {
            Ok(DidDocument {
                context: None,
                id: did.as_ref().to_string(),
                also_known_as: None,
                verification_method: None,
                service: Some(vec![Service {
                    id: String::from("#atproto_pds"),
                    r#type: String::from("AtprotoPersonalDataServer"),
                    service_endpoint: String::from("https://aud.example.com"),
                }]),
            })
        }
    }

    pub struct NoopHandleResolver;

    impl Resolver for NoopHandleResolver {
        type Input = Handle;
        type Output = Did;
        type Error = atrium_identity::Error;
        async fn resolve(&self, _: &Self::Input) -> Result<Self::Output, Self::Error> {
            unimplemented!()
        }
    }

    pub fn oauth_resolver<T>(
        http_client: Arc<T>,
    ) -> OAuthResolver<T, MockDidResolver, NoopHandleResolver>
    where
        T: HttpClient + Send + Sync,
    {
        OAuthResolver::new(
            OAuthResolverConfig {
                did_resolver: MockDidResolver,
                handle_resolver: NoopHandleResolver,
                authorization_server_metadata: Default::default(),
                protected_resource_metadata: Default::default(),
            },
            http_client,
        )
    }

    pub fn dpop_key() -> Key {
        serde_json::from_str(
            r#"{
                "kty": "EC",
                "crv": "P-256",
                "x": "NIRNgPVAwnVNzN5g2Ik2IMghWcjnBOGo9B-lKXSSXFs",
                "y": "iWF-Of43XoSTZxcadO9KWdPTjiCoviSztYw7aMtZZMc",
                "d": "9MuCYfKK4hf95p_VRj6cxKJwORTgvEU3vynfmSgFH2M"
            }"#,
        )
        .expect("key should be valid")
    }

    pub fn server_metadata() -> OAuthAuthorizationServerMetadata {
        OAuthAuthorizationServerMetadata {
            issuer: String::from("https://iss.example.com"),
            token_endpoint: String::from("https://iss.example.com/token"),
            token_endpoint_auth_methods_supported: Some(vec![
                String::from("none"),
                String::from("private_key_jwt"),
            ]),
            ..Default::default()
        }
    }

    pub fn client_metadata() -> OAuthClientMetadata {
        AtprotoLocalhostClientMetadata::default()
            .try_into_client_metadata(&None)
            .expect("client metadata should be valid")
    }

    pub fn protected_resource_metadata() -> OAuthProtectedResourceMetadata {
        OAuthProtectedResourceMetadata {
            resource: String::from("https://aud.example.com"),
            authorization_servers: Some(vec![String::from("https://iss.example.com")]),
            ..Default::default()
        }
    }
}
