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::{UBigInt, BigInt};
use crate::common::FromDB;
use crate::Response;

#[allow(dead_code)]
pub struct Invite {
    pub id: BigInt,
    pub uses: Option<BigInt>,
    pub expires: bool
}

#[async_trait]
impl FromDB<Invite> for Invite {
    type Row = Option<(BigInt, Option<BigInt>, bool)>;

    async fn get(p: &Pool, id: UBigInt) -> Response<Self> {
        if id <= 0 {
            return Response::Empty;
        }

        if let Ok(conn) = p.get_conn().await {
            let q = "SELECT id, uses, expires FROM invites WHERE id = :id ";
            let result: Result<(Conn, Self::Row), SqlError> = 
                conn.first_exec(q, params!{"id" => id}).await;

            if let Ok((_, row)) = result {
                return match row {
                    Some(row) => Response::Row(Self {
                        id: id as BigInt,
                        uses: row.1,
                        expires: row.2
                    }),
                    None => Response::Empty
                }
            }
        }
        return Response::Empty;
    }

    async fn update(p: &Pool, row: Self) -> Response<Self> {
        let q = r#"UPDATE invites
        SET uses = :uses, expires: :exp
        WHERE id = :id
        "#;
        // forcibly udpate even if theres nothing there
        // this way we avoid doing an extra network hit
        if row.id <= 0 {
            return Response::Empty;
        }

        if let Ok(conn) = p.get_conn().await {
            let result: Result<Conn, SqlError> =
                conn.drop_exec(q, params!{
                    "id" => row.id,
                    "uses" => row.uses,
                    "exp" => row.expires
                }).await;

            return match result {
                Ok(_) => Response::Success,
                Err(_) => Response::Other(format!("Could not update entry {}", row.id))
            }
        }
        return Response::Empty;
    }
    async fn delete(p: &Pool, id: UBigInt) -> Response<Self> {
        if id <= 0 { // really lame "assertion" that each method has to use for invites since they all internally use 
            return Response::Empty;
        }
        if let Ok(conn) = p.get_conn().await {
            let q = "DELETE FROM invites WHERE id = :id";
            let result: Result<Conn, SqlError> =
                conn.drop_exec(q, params!{"id" => id as BigInt}).await;
            return match result {
                Ok(_) => Response::Success,
                Err(_) => Response::Other(format!("Could not delete {}", id))
            }
        }
        return Response::Success;
    }
}
