use mysql_async::{params, Pool, Conn};
use mysql_async::prelude::Queryable;
use mysql_async::error::Error as SqlError;

use async_trait::async_trait;

use crate::{Response, no_conn, sql_err};
use crate::{UBigInt, BigInt, Integer, VarChar};

use crate::common::{FromDB};

#[allow(dead_code)] // only because some fields are read by the user
pub struct Member {
    pub id: UBigInt,
    pub secret: VarChar,
    pub name: VarChar,
    pub joindate: BigInt,
    pub status: Integer,
    pub permissions: UBigInt,
}

/*
 *
 * conn = getconn
 * result = conn.query
 * return response based on result
 *
 */
#[async_trait]
impl FromDB<Member> for Member {
    type Row = Option<(VarChar, VarChar, BigInt, Integer, UBigInt)>;

    async fn get(p: &Pool, id: UBigInt) -> Response<Self> {
        if let Ok(conn) = p.get_conn().await {
            let q = "SELECT secret, name, joindate, status, permissions FROM members WHERE id = :id";
            let db_res : Result<(Conn, Self::Row), SqlError> 
                        = conn.first_exec(q, params!{"id" => id}).await;
            if let Ok((_, row)) = db_res {
                return match row {
                    Some(row) => Response::Row(Self {
                        id: id,
                        secret: row.0,
                        name: row.1,
                        joindate: row.2,
                        status: row.3,
                        permissions: row.4
                    }),
                    None => Response::Empty
                }
            }
            return Response::Other(sql_err!("Fetch failed"));
        }
        return Response::Other(no_conn!("Member::FromDB::get"));
    }

    async fn update(p: &Pool, row: Member) -> Response<Self> {
        let q = r#"UPDATE members
                SET status = :status, permissions = :perms, name = :name
                WHERE id = :id"#;
        // TODO: remoev this retarded extra network hit
        // technically this is really nice for the user but ultimately it either works or it
        // doesn't so like the fucks the point of doing some bs like this
        // the next a client queries this they'll get whatever data is in the db
        // likewise clients are going likely going to re-get this data since its been updated
        // or at least should
        if let Ok(conn) = p.get_conn().await {
            match Member::get(p, row.id).await {
                Response::Row(_) => {
                    let db_result: Result<Conn, SqlError>
                        = conn.drop_exec(q, params!{
                            "id" => row.id,
                            "status" => row.status,
                            "name" => row.name,
                            "perms" => row.permissions
                        }).await;
                    match db_result {
                        Ok(_) => return Response::Success,
                        Err(_) => return Response::Other(sql_err!("WARN member found | Member::FromDB::update failed - "))
                    }
                },
                Response::Empty => return Response::Empty,
                Response::Other(msg) => return Response::Other(msg),
                _ => return Response::Other(sql_err!("Member::FromD::update Bro i dont even"))
            }
        }
        return Response::Other(no_conn!("Member::FromDB::update"));
    }

    async fn delete(p: &Pool, id: UBigInt) -> Response<Self> {
        if let Ok(conn) = p.get_conn().await {
            let q = "DELETE from members WHERE id = :id";
            let db_result: Result<Conn, SqlError>
                = conn.drop_exec(q, params!{"id" => id}).await;
            match Member::get(p, id).await {
                Response::Row(_) => {
                    if let Ok(conn) = db_result {
                        return match conn.prep_exec("", params!{"id" => id}).await {
                            Ok(_) => Response::Success,
                            Err(_) => Response::Other(sql_err!("Member::FromDB::delete"))
                        }
                    }
                    return Response::Success
                },
                Response::Empty => return Response::Empty,
                _ => return Response::Other(sql_err!("Member::FromDB::delete | another stupid get happened delid this"))
            }
        }

        return Response::Empty;
    }
}
