use lazy_static::lazy_static;
use std::time::{SystemTime, UNIX_EPOCH};

use isahc::{cookies::CookieJar, prelude::*, Request};
use std::sync::RwLock;

struct CookieHold {
    pub jar: CookieJar,
}

lazy_static! {
    /// Stores a client with a persistent cookie store
    static ref SESSION_COOKIE: RwLock<CookieHold> = RwLock::new(CookieHold { jar: CookieJar::new()});
    /// Stores the last time a request was made, in seconds since UNIX epoch
    static ref SESSION_TIME: RwLock<u64> = RwLock::new(0);
}

/// Makes a request to the online classroom, and returns the html string
pub async fn request(url: String) -> Result<String, String> {
    let classroom_url = std::env::var("CLASSROOM_URL").expect("CLASSROOM_URL env var is not set!");

    ensure_session().await?;

    // Get the stored client
    let jar = SESSION_COOKIE.read().unwrap().jar.clone();

    let uri = format!("{}{}", classroom_url, url);

    // Do the request
    let response = Request::get(uri)
        .cookie_jar(jar.clone())
        .body(())
        .or_else(|err| Err(format!("Error creating request: {:?}", err)))?
        .send();

    let mut response = match response {
        Ok(r) => r,
        Err(err) => return Err(format!("Error sending request: {:?}", err)),
    };

    match response.text() {
        Ok(t) => Ok(t),
        Err(err) => Err(format!("Error getting text from response: {:?}", err)),
    }
}

/// Makes sure that the session cookie is set, and that it is valid
pub async fn ensure_session() -> Result<(), String> {
    let last_usage_time = SESSION_TIME.read().unwrap().clone();

    // Get current time in seconds
    let current_time = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards")
        .as_secs();

    let time_passed = current_time - last_usage_time;

    // Default PHP session timeout is 1440 seconds. Use a 1400 seconds timeout to be safe
    if time_passed > 1400 {
        login().await?;

        let mut time_ptr = SESSION_TIME.write().unwrap();
        *time_ptr = current_time;
    }

    Ok(())
}

/// Logins to the online classroom, and sets the session cookie
async fn login() -> Result<(), String> {
    let classroom_url = std::env::var("CLASSROOM_URL").expect("CLASSROOM_URL env var is not set!");
    let classroom_user =
        std::env::var("CLASSROOM_USER").expect("CLASSROOM_USER env var is not set!");
    let classroom_password =
        std::env::var("CLASSROOM_PASSWORD").expect("CLASSROOM_PASSWORD env var is not set!");

    let jar = CookieJar::new();

    let response = Request::post(format!("{}/index.php", classroom_url))
        .header("Content-Type", "application/x-www-form-urlencoded")
        .cookie_jar(jar.clone())
        .body(format!(
            "login={}&password={}&submitAuth=&_qf__formLogin=",
            classroom_user, classroom_password
        ))
        .unwrap()
        .send();

    match response {
        Ok(_) => {
            SESSION_COOKIE.write().unwrap().jar = jar.clone();
            Ok(())
        }
        Err(error) => Err(format!("Error connecting to online classroom: {:?}", error)),
    }
}
