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)]
pub struct Message {
    pub id: UBigInt,
    pub time: BigInt,
    pub content: VarChar,
    pub author_id: UBigInt,
    pub channel_name: VarChar
}

#[async_trait]
impl FromDB<Message> for Message {
    type Row = Option<(UBigInt, BigInt, VarChar, UBigInt, VarChar)>;

    async fn get(p: &Pool, id: UBigInt) -> Response<Self> {
        if let Ok(conn) = p.get_conn().await {
            let q = "SELECT id, time, content, author_id, channel_name 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,
                        time: row.1,
                        content: row.2,
                        author_id: row.3,
                        channel_name: row.4
                    }),
                    None => Response::Empty
                }
            }
            return Response::Other(sql_err!("Message::FromDB::get"));
        }
        return Response::Other(no_conn!("Message"));
    }

    async fn update(p: &Pool, row: Self) -> Response<Self> {
        // NOTE: we only allow the changing of content in this since
        // no other column has good reason to be modified
        if let Ok(conn) = p.get_conn().await {
            let q = "UPDATE messages
                SET content = :content
                WHERE id = :id";
            let result: Result<Conn, SqlError> =
                conn.drop_exec(q, params!{"id" => row.id, "content" => row.content}).await;
            return match result {
                Ok(_) => Response::Success,
                Err(_) => Response::Other(sql_err!("Message::FromDB::update"))
            }
        }
        return Response::Other(no_conn!("Message::FromDB::update"))
    }

    async fn delete(p: &Pool, id: UBigInt) -> Response<Self> {
        if let Ok(conn) = p.get_conn().await {
            let q = "DELETE FROM messages WHERE id = :id";
            let result: Result<Conn, SqlError> =
                conn.drop_exec(q, params!{"id" => id}).await;
            return match result {
                Ok(_) => Response::Success,
                Err(_) => Response::Other(sql_err!("Message::FromDB::delete"))
            }
        }
        return Response::Other(no_conn!("Message::FromDB::update"))
    }
}
