use actix_identity::Identity;
use actix_web::{get, post, web, HttpResponse, HttpRequest, ResponseError, HttpMessage};
use siren::ServiceError;

use crate::db::users::{LoginAuth, RegisterUser, InsertUser, QueryUser, verify, LoggedUser};

#[post("/register")]
async fn register(user: web::Json<RegisterUser>) -> HttpResponse {
  let register_user = user.0;
  let insert_user: InsertUser = match register_user.convert_to_insert() {
    Ok(user) => user,
    Err(err) => return ResponseError::error_response(&err)
  };
  match InsertUser::insert(insert_user) {
    Ok(_) => {
      HttpResponse::Created().finish()
    },
    Err(err) => {
      // Obfuscate the service error message to prevent leaking database details
      if err.status == 409 {
        return HttpResponse::Conflict().finish();
      } else {
        return ResponseError::error_response(&err);
      }
    }
  }
}

#[post("/login")]
async fn login(req: HttpRequest, auth: web::Json<LoginAuth>) -> HttpResponse {
  let email = auth.email.clone();

  let query_user = match QueryUser::get_by_email(&email) {
    Ok(query_user) => query_user,
    Err(err) => return ResponseError::error_response(&err)
  };
  let hash = query_user.hash;
  let password = auth.password.as_bytes();
  match verify(&hash, password) {
    Ok(_) => {
      let user = LoggedUser {
        email: email.clone()
      };
      let user_string = serde_json::to_string(&user).unwrap();
      match Identity::login(&req.extensions(), user_string) {
        Ok(_) => HttpResponse::Ok().finish(),
        Err(err) => return ResponseError::error_response(&err)
      }
    },
    Err(err) => ResponseError::error_response(&ServiceError {
      status: 401,
      message: err.to_string()
    })
  }
}

#[post("/logout")]
async fn logout(identity: Identity) -> HttpResponse {
  identity.logout();
  HttpResponse::Ok().finish()
}

#[get("/ping")]
async fn ping(user: LoggedUser) -> HttpResponse {
  HttpResponse::Ok().json(user)
}

pub fn init_routes(config: &mut web::ServiceConfig) {
  config.service(web::scope("users")
    .service(register)
    .service(login)
    .service(logout)
    .service(ping));
}