use serde::{Deserialize, Serialize};

#[cfg(feature = "server")]
use crate::util::surrealdb::DB;
#[cfg(feature = "server")]
use surrealdb::sql::statements::{BeginStatement, CommitStatement};

#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
pub struct User {
    pub id: String,
    email: String,
    password: Option<String>,
}

#[cfg(feature = "server")]
impl User {
    pub async fn new(
        email: String,
        password: String,
        member_ids: Vec<String>,
    ) -> Result<Self, crate::Error> {
        // Create user record
        let mut transaction = DB.query(BeginStatement::default())
            .query("let $user = CREATE ONLY user SET email = $email, password = crypto::argon2::generate($password) RETURN record::id(id) as id, email;")
            .bind(("email", email))
            .bind(("password", password));

        // Link user with members
        for member_id in member_ids {
            transaction = transaction.query(format!("RELATE ONLY (type::thing('user', $user.id))->user_to_member->(type::thing('member', $member_id_{}));", member_id))
                .bind((format!("member_id_{}", member_id), member_id));
        }

        let mut res = transaction
            .query("RETURN $user")
            .query(CommitStatement::default())
            .await?;

        let user: Option<Self> = res.take(0)?;

        match user {
            Some(u) => Ok(u),
            None => Err(crate::Error::NoDocument),
        }
    }

    pub async fn verify_credentials(
        email: String,
        password: String,
    ) -> Result<String, crate::Error> {
        let mut res = DB
            .query("SELECT VALUE record::id(id) as id FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)")
            .bind(("email", email)).bind(("password", password))
            .await?;

        let id: Option<String> = res.take(0)?;

        match id {
            Some(i) => Ok(i),
            None => Err(crate::Error::NoDocument),
        }
    }
}
