Documentation
// use actix_http::header::HeaderMap;
// use actix_web::HttpRequest;
use anyhow;
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};

// #[async_trait::async_trait]
// pub trait TokenPermission {
//     async fn check_and_verify(&self, req: (HeaderMap, String)) -> anyhow::Result<AccessToken>;
// }

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AccessToken {
    pub user_id: u64,         // 用户ID
    pub user_name: String,    // 用户姓名
    pub user_account: String, // 用户账户
    pub app_id: String,       // 租户信息
    pub platform: String,     // 平台信息
    pub is_admin: bool,       // 是否是对应租户下的Admin
    pub is_super: bool,       // 是否是超级管理员
    pub exp: u64,
}

impl AccessToken {
    pub fn encode_token(
        user_id: u64,
        user_account: &String,
        user_name: &String,
        app_id: &String,
        platform: &String,
        is_admin: bool,
        is_super: bool,
        timeout_hour: u16,
        secret: &str,
    ) -> anyhow::Result<String> {
        let header = Header::new(Algorithm::HS512);
        let my_claims = AccessToken {
            user_id: user_id.clone(),
            user_account: user_account.clone(),
            user_name: user_name.clone(),
            app_id: app_id.clone(),
            platform: platform.clone(),
            is_admin: is_admin,
            is_super: is_super,
            exp: (std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs()
                + 60 * 60 * (timeout_hour as u64)),
        };
        if let Ok(token) = encode(
            &header,
            &my_claims,
            &EncodingKey::from_secret(secret.as_ref()),
        ) {
            return anyhow::Ok(token);
        } else {
            return Err(anyhow::anyhow!("encode_token with error"));
        }
    }

    pub fn decode_token(token: &String, secret: &str) -> anyhow::Result<Self> {
        if let Ok(decode_data) = decode::<Self>(
            &token,
            &DecodingKey::from_secret(secret.as_ref()),
            &Validation::new(Algorithm::HS512),
        ) {
            return anyhow::Ok(decode_data.claims);
        } else {
            return Err(anyhow::anyhow!("decode_token with error"));
        }
    }

    pub fn is_expired(&self) -> bool {
        let cur_time = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();
        if self.exp < cur_time {
            true
        } else {
            false
        }
    }
}

pub const ACCESS_TOKEN_TIME: u16 = 2 * 24;
pub const REFRESH_TOKEN_TIME: u16 = 7 * 24;

pub fn create_access_token(
    user_id: u64,
    user_account: &String,
    user_name: &String,
    app_id: &String,
    platform: &String,
    is_admin: bool,
    is_super: bool,
    secret: &str,
) -> anyhow::Result<String> {
    AccessToken::encode_token(
        user_id,
        user_account,
        user_name,
        app_id,
        platform,
        is_admin,
        is_super,
        ACCESS_TOKEN_TIME,
        secret,
    )
}

pub fn create_refresh_token(
    user_id: u64,
    user_account: &String,
    user_name: &String,
    app_id: &String,
    platform: &String,
    is_admin: bool,
    is_super: bool,
    secret: &str,
) -> anyhow::Result<String> {
    AccessToken::encode_token(
        user_id,
        user_account,
        user_name,
        app_id,
        platform,
        is_admin,
        is_super,
        REFRESH_TOKEN_TIME,
        secret,
    )
}

// pub fn get_token_and_path(req: &HttpRequest) -> (HeaderMap, String) {
//     let headers = req.headers();
//     let reqpath = format!("{} {}", req.method(), req.path());
//     (headers.clone(), reqpath)
// }