use crate::Query;
use axum::async_trait;
use axum_login::{secrecy::SecretVec, AuthUser, UserStore};
use sea_orm::*;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxumUser {
    pub id: i32,
    pub password_hash: String,
    pub name: String,
}

type Result<T = ()> = std::result::Result<T, eyre::Error>;

impl<Role> AuthUser<Role> for AxumUser
where
    Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
{
    fn get_id(&self) -> String {
        format!("{}", self.id)
    }

    fn get_password_hash(&self) -> axum_login::secrecy::SecretVec<u8> {
        SecretVec::new(self.password_hash.clone().into())
    }
}

#[derive(Debug, Clone)]
pub struct AxumUserStore {
    conn: DatabaseConnection,
}

impl AxumUserStore {
    pub fn new(conn: &DatabaseConnection) -> Self {
        Self { conn: conn.clone() }
    }
}

#[async_trait]
impl<Role> UserStore<Role> for AxumUserStore
where
    Role: PartialOrd + PartialEq + Clone + Send + Sync + 'static,
{
    type User = AxumUser;

    async fn load_user(&self, user_id: &str) -> Result<Option<Self::User>> {
        // my user id is a Vec<u8>, so it's stored base64 encoded
        let id: i32 = user_id.parse().unwrap();
        let user = Query::find_user_by_id(&self.conn, id).await?;
        match user {
            Some(u) => Ok(Some(AxumUser {
                id: u.id,
                password_hash: u.password_hash,
                name: u.user_name.unwrap(),
            })),
            None => Ok(None),
        }
    }
}
