use rocket::Outcome;
use rocket::http::{ Status, RawStr };
use rocket::request::{ self, Request, FromRequest, FromParam };

use bson::{ bson, doc, ordered::OrderedDocument };
use ulid::Ulid;

use crate::database;

pub struct User(
	pub Ulid,
	pub String,
	pub OrderedDocument,
);

#[derive(Debug)]
pub enum AuthError {
    BadCount,
    Missing,
    Invalid,
}

impl<'a, 'r> FromRequest<'a, 'r> for User {
    type Error = AuthError;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        let keys: Vec<_> = request.headers().get("x-auth-token").collect();
        match keys.len() {
			0 => Outcome::Failure((Status::Forbidden, AuthError::Missing)),
			1 => {
				let key = keys[0];
				let col = database::get_db().collection("users");
				let result = col.find_one(doc! { "access_token": key }, None).unwrap();

				if let Some(user) = result {
					Outcome::Success(User(
						Ulid::from_string(user.get_str("_id").unwrap()).unwrap(),
						user.get_str("username").unwrap().to_string(),
						user
					))
				} else {
					Outcome::Failure((Status::Forbidden, AuthError::Invalid))
				}
			},
            _ => Outcome::Failure((Status::BadRequest, AuthError::BadCount)),
        }
    }
}

impl<'r> FromParam<'r> for User {
    type Error = &'r RawStr;

    fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
		let col = database::get_db().collection("users");
		let result = col.find_one(doc! { "_id": param.to_string() }, None).unwrap();

		if let Some(user) = result {
			Ok(User(
				Ulid::from_string(user.get_str("_id").unwrap()).unwrap(),
				user.get_str("username").unwrap().to_string(),
				user
			))
		} else {
			Err(param)
		}
    }
}