#[cfg(feature = "server")]
use crate::util::surrealdb::DB;
use dioxus::prelude::*;

use serde::{Deserialize, Serialize};

use super::user::User;

#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
pub struct Session {
    id: String,
    user_id: String,
    expires: i64,
    token: String,
}

#[cfg(feature = "server")]
impl Session {
    pub async fn new(user_id: String) -> Result<Self, crate::Error> {
        let mut res = DB
            .query("CREATE ONLY session SET user = type::thing('user', $user_id) RETURN record::id(id) as id, record::id(user) as user_id, time::unix(expires) as expires, token;")
            .bind(("user_id", user_id))
            .await?;

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

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

    pub fn set_cookie(&self) -> Result<(), crate::Error> {
        use axum::http::{header, HeaderValue};
        use axum_extra::extract::cookie::{Cookie, Expiration, SameSite};
        use dioxus::prelude::server_context;
        use time::OffsetDateTime;

        let timestamp = OffsetDateTime::from_unix_timestamp(self.expires)?;
        let expiration = Expiration::from(timestamp);

        let mut cookie = Cookie::build(("session_token", &self.token))
            .expires(expiration)
            .build();

        cookie.set_same_site(SameSite::Strict);

        server_context()
            .response_parts_mut()
            .unwrap()
            .headers
            .insert(
                header::SET_COOKIE,
                HeaderValue::from_str(&cookie.to_string())?,
            );

        Ok(())
    }

    pub async fn fetch_user_from_token(session_token: String) -> Result<User, crate::Error> {
        let mut res = DB
            .query("SELECT record::id(user) as id, user.email as email FROM session WHERE token = $session_token")
            .bind(("session_token", session_token))
            .await?;

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

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

    pub async fn get_token_from_cookie() -> Result<String, crate::Error> {
        use axum_extra::extract::CookieJar;

        let jar: CookieJar = extract().await.unwrap_or_default();

        match jar.get("session_token") {
            Some(s) => Ok(s.value().to_string()),
            None => Err(crate::Error::NoSessionCookie),
        }
    }
}
