use actix_identity::Identity;
use actix_web::dev::Payload;
use actix_web::{Error, FromRequest, HttpMessage, HttpRequest};
use futures_util::future::{Ready, ready};
use std::fmt::Display;

pub struct AuthExtractor {
    identity: Option<Identity>,
    request: HttpRequest,
}

impl AuthExtractor {
    /// Check whether the user is authenticated or not
    pub fn is_authenticated(&self) -> bool {
        self.identity.is_some()
    }

    /// Authenticate the user
    pub fn authenticate(&self, id: impl Display) {
        Identity::login(&self.request.extensions(), id.to_string())
            .expect("Unable to set authentication!");
    }

    pub fn id(&self) -> Option<String> {
        self.identity.as_ref().map(|i| i.id().unwrap())
    }

    pub fn sign_out(self) {
        if let Some(i) = self.identity {
            i.logout()
        }
    }
}

impl FromRequest for AuthExtractor {
    type Error = Error;
    type Future = Ready<Result<Self, Error>>;

    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        let identity: Option<Identity> = Identity::from_request(req, payload).into_inner().ok();

        ready(Ok(Self {
            identity,
            request: req.clone(),
        }))
    }
}