use std::collections::HashMap;
use hyper::{Response, Body, StatusCode};
use mysql_async::Pool;
use serde_json::json;

use db::member::STATUS_ONLINE;
use db::Member;
use db::Response::*;

use crate::http::set_json_body;
use crate::qs_param;


pub async fn get_online_members(p: &Pool, response: &mut Response<Body>) {
    // TODO: at some point we should provide a way of not querying literally every user in
    // existance
    // TODO: loggin at some point or something idklol
    return match Member::filter(p, STATUS_ONLINE).await {
        Set(users) => {
            set_json_body(response, json!(users));
        },
        db::Response::Other(e) => {
            eprintln!("{}", e);
            *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
        },
        _ => *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR
    }
}

pub async fn get_self(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
    let uid = qs_param!(params, "id", u64).unwrap();

    match Member::get(p, uid).await {
        Row(user) => {
            set_json_body(response, json!({
                "id" : user.id,
                "name" : user.name,
                "permissions" : user.permissions
            }));
        },
        Other(e) => eprintln!("{}", e),
        _ => {
            *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
        }
    };
}

pub async fn post_self_nickname(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
    /*
     * @param nick <String>
     * @respond StatusCode
     *
     * Prety straightforward response here as its literally just the status code
     * Assuming we have good input
     */

    let nick = qs_param!(params, "nick", String);

    *response.status_mut() = if let Some(nick) = nick {

        let uid = qs_param!(params, "id", u64).unwrap();
        match Member::update_nick(p, uid, nick.as_ref()).await {
            Ok(_) => StatusCode::OK,
            Err(e) => {
                eprintln!("{}", e);
                StatusCode::INTERNAL_SERVER_ERROR
            }
        }
    } else {
        StatusCode::BAD_REQUEST
    }
}

pub async fn get_member(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
    let target = qs_param!(params, "member_id", u64);

    *response.status_mut() = if let Some(target) = target{
        match Member::get(p, target).await {
            Row(member) => {
                set_json_body(response, json!({"member": {
                    "id": member.id,
                    "name": member.name,
                    "status": member.status, 
                    "permissions": member.permissions
                }}));
                StatusCode::OK
            },
            Empty => StatusCode::NOT_FOUND,
            Other(_msg) => StatusCode::INTERNAL_SERVER_ERROR,
            _ => StatusCode::INTERNAL_SERVER_ERROR
        }
    } else {
        StatusCode::BAD_REQUEST
    }
}

#[cfg(test)]
mod tests {
    use crate::testing::get_pool;
    use crate::testing::hyper_resp;
    use std::collections::HashMap;
    use tokio;

    #[tokio::test]
    async fn get_member_test() {
        // mostly looking for correct response code here
        // NOTE: we are assuming that the database we're trying against has 
        // some dummy data in it for us to actually look at

        let p = get_pool();
        let mut resp = hyper_resp();
        let mut params: HashMap<String, String> = HashMap::new();
        params.insert("member_id".into(), "1".into());

        super::get_member(&p, &mut resp, params).await;

        println!("Response: {:?}", resp);
    }
}
