use crate::authentication::{IdentityProvider, IdentityProviderHandler};
use oauth2_client::re_exports::RedirectUri;
use std::env;
use std::error::Error;
use strum::IntoEnumIterator;

pub fn new(idp: IdentityProvider) -> Box<dyn IdentityProviderHandler> {
    match idp {
        #[cfg(feature = "authentication-amazon")]
        IdentityProvider::Amazon => Box::new(super::amazon::AmazonIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-apple")]
        IdentityProvider::Apple => Box::new(super::apple::AppleIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-digitalocean")]
        IdentityProvider::DigitalOcean => {
            Box::new(super::digital_ocean::DigitalOceanIdentityProviderHandler::default())
        }

        #[cfg(feature = "authentication-facebook")]
        IdentityProvider::Facebook => Box::new(super::facebook::FacebookIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-github")]
        IdentityProvider::GitHub => Box::new(super::github::GitHubIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-gitlab")]
        IdentityProvider::GitLab => Box::new(super::gitlab::GitLabIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-google")]
        IdentityProvider::Google => Box::new(super::google::GoogleIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-instagram")]
        IdentityProvider::Instagram => Box::new(super::instagram::InstagramIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-linode")]
        IdentityProvider::Linode => Box::new(super::linode::LinodeIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-microsoft")]
        IdentityProvider::Microsoft => Box::new(super::microsoft::MicrosoftIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-okta")]
        IdentityProvider::Okta => Box::new(super::okta::OktaIdentityProviderHandler::default()),

        #[cfg(feature = "authentication-twitter")]
        IdentityProvider::Twitter => Box::new(super::twitter::TwitterIdentityProviderHandler::default()),
    }
}

pub struct IdentityProviderHandlerInfo {
    pub idp: IdentityProvider,
    pub handler: Box<dyn IdentityProviderHandler>,
    pub client_secret: String,
    pub redirect_uri: RedirectUri,
}

#[allow(unused)]
pub fn new_from_env_for_client_id(client_id: &str) -> Result<IdentityProviderHandlerInfo, Box<dyn Error>> {
    for idp in IdentityProvider::iter() {
        let client_id_env_var_name = format!("{}_CLIENT_ID", idp.as_ref().to_uppercase());
        if let Ok(client_id_) = env::var(&client_id_env_var_name) {
            if client_id != client_id_ {
                continue;
            }

            let client_secret_env_var_name = format!("{}_CLIENT_SECRET", idp.as_ref().to_uppercase());
            let client_secret = match env::var(&client_secret_env_var_name) {
                Ok(secret) => secret,
                Err(e) => {
                    let msg =
                        format!("Client is attempting {idp} login, but {client_secret_env_var_name} is not configured");
                    log::error!("{msg}");
                    return Err(e.into());
                }
            };

            let redirect_url_env_var_name = format!("{}_REDIRECT_URL", idp.as_ref().to_uppercase());
            let redirect_uri = match env::var(&redirect_url_env_var_name) {
                Ok(redirect_url) => redirect_url.parse()?,
                Err(e) => {
                    let msg =
                        format!("Client is attempting {idp} login, but {redirect_url_env_var_name} is not configured");
                    log::error!("{msg}");
                    return Err(e.into());
                }
            };

            let handler = new(idp.clone());
            return Ok(IdentityProviderHandlerInfo {
                idp,
                handler,
                client_secret,
                redirect_uri,
            });
        }
    }

    Err("Unsupported IdP".into())
}
