use crate::ayb_db::db_interfaces::AybDb;
use crate::ayb_db::models::{AuthenticationMethodStatus, AuthenticationMethodType};
use crate::email::{backend::EmailBackends, send_registration_email};
use crate::error::AybError;

use crate::http::structs::{AuthenticationDetails, EmptyResponse};
use crate::server::config::AybConfig;
use crate::server::tokens::encrypt_auth_token;
use crate::server::utils::get_lowercased_header;
use crate::server::web_frontend::WebFrontendDetails;
use actix_web::{post, web, HttpRequest, HttpResponse};

#[post("/v1/log_in")]
async fn log_in(
    req: HttpRequest,
    ayb_db: web::Data<Box<dyn AybDb>>,
    ayb_config: web::Data<AybConfig>,
    web_details: web::Data<Option<WebFrontendDetails>>,
    email_backends: web::Data<EmailBackends>,
) -> Result<HttpResponse, AybError> {
    let entity = get_lowercased_header(&req, "entity")?;
    let desired_entity = ayb_db.get_entity_by_slug(&entity).await;

    if let Ok(instantiated_entity) = desired_entity {
        let auth_methods = ayb_db
            .list_authentication_methods(&instantiated_entity)
            .await?;
        for method in auth_methods {
            if AuthenticationMethodType::try_from(method.method_type)?
                == AuthenticationMethodType::Email
                && AuthenticationMethodStatus::try_from(method.status)?
                    == AuthenticationMethodStatus::Verified
            {
                let token = encrypt_auth_token(
                    &AuthenticationDetails {
                        version: 1,
                        entity,
                        entity_type: instantiated_entity.entity_type,
                        email_address: method.email_address.to_owned(),
                    },
                    &ayb_config.authentication,
                )?;
                send_registration_email(
                    &email_backends,
                    &ayb_config.email,
                    &method.email_address,
                    &token,
                    web_details.get_ref(),
                )
                .await?;
                return Ok(HttpResponse::Ok().json(EmptyResponse {}));
            }
        }
    }

    Err(AybError::Other {
        message: format!("No account or email authentication method for {entity}"),
    })
}
