use sqlx::{
    AssertSqlSafe, FromRow, Postgres, Transaction,
    postgres::{PgArguments, PgQueryResult, PgRow},
};

pub struct AppTransaction<'a> {
    pub inner: Transaction<'a, Postgres>,
}

impl<'a> AppTransaction<'a> {
    pub fn inner_mut(&mut self) -> &mut Transaction<'a, Postgres> {
        &mut self.inner
    }
    pub async fn commit(self) -> Result<(), sqlx::Error> {
        self.inner.commit().await
    }
    pub async fn rollback(self) -> Result<(), sqlx::Error> {
        self.inner.rollback().await
    }
    pub async fn execute(
        &mut self,
        sql: &str,
    ) -> Result<PgQueryResult, sqlx::Error> {
        self.execute_with_args(sql, PgArguments::default()).await
    }
    pub async fn execute_with_args(
        &mut self,
        sql: &str,
        args: PgArguments,
    ) -> Result<PgQueryResult, sqlx::Error> {
        sqlx::query_with(AssertSqlSafe(sql.to_owned()), args)
            .execute(&mut *self.inner)
            .await
    }
    pub async fn fetch_one<T>(&mut self, sql: &str) -> Result<T, sqlx::Error>
    where
        for<'r> T: FromRow<'r, PgRow> + Send + Unpin,
    {
        self.fetch_one_with_args(sql, PgArguments::default()).await
    }
    pub async fn fetch_one_with_args<T>(
        &mut self,
        sql: &str,
        args: PgArguments,
    ) -> Result<T, sqlx::Error>
    where
        for<'r> T: FromRow<'r, PgRow> + Send + Unpin,
    {
        sqlx::query_as_with::<_, T, _>(AssertSqlSafe(sql.to_owned()), args)
            .fetch_one(&mut *self.inner)
            .await
    }
    pub async fn fetch_optional<T>(
        &mut self,
        sql: &str,
    ) -> Result<Option<T>, sqlx::Error>
    where
        for<'r> T: FromRow<'r, PgRow> + Send + Unpin,
    {
        self.fetch_optional_with_args(sql, PgArguments::default())
            .await
    }

    pub async fn fetch_optional_with_args<T>(
        &mut self,
        sql: &str,
        args: PgArguments,
    ) -> Result<Option<T>, sqlx::Error>
    where
        for<'r> T: FromRow<'r, PgRow> + Send + Unpin,
    {
        sqlx::query_as_with::<_, T, _>(AssertSqlSafe(sql.to_owned()), args)
            .fetch_optional(&mut *self.inner)
            .await
    }
    pub async fn fetch_all<T>(
        &mut self,
        sql: &str,
    ) -> Result<Vec<T>, sqlx::Error>
    where
        for<'r> T: FromRow<'r, PgRow> + Send + Unpin,
    {
        self.fetch_all_with_args(sql, PgArguments::default()).await
    }

    pub async fn fetch_all_with_args<T>(
        &mut self,
        sql: &str,
        args: PgArguments,
    ) -> Result<Vec<T>, sqlx::Error>
    where
        for<'r> T: FromRow<'r, PgRow> + Send + Unpin,
    {
        sqlx::query_as_with::<_, T, _>(AssertSqlSafe(sql.to_owned()), args)
            .fetch_all(&mut *self.inner)
            .await
    }
}
