pub mod read_repository;
pub mod create_repository;

pub mod update_repository;

pub mod delete_repository;

use sqlx::{Database, FromRow};
use crate::error::OrmError;
use crate::traits::entity::Entity;

#[async_trait::async_trait]
pub trait Repository where Self: Entity + Sized + for <'r> FromRow<'r, <sqlx::Any as Database>::Row> + Send + Unpin + Sync {
    async fn query<R>(sql_query: String) -> Result<R, OrmError> where R: Entity + Sized + for <'r> FromRow<'r, <sqlx::Any as Database>::Row> + Send + Unpin + Sync;
    async fn execute(sql_query: String) -> Result<(), OrmError>;
}

#[async_trait::async_trait]
impl <T> Repository for T where T: Entity + Sized + for <'r> FromRow<'r, <sqlx::Any as Database>::Row> + Send + Unpin + Sync {
    
    async fn query<R>(sql_query: String) -> Result<R, OrmError> where R: Entity + Sized + for <'r> FromRow<'r, <sqlx::Any as Database>::Row> + Send + Unpin + Sync {
        let result = sqlx::query_as::<_, R>(sql_query.as_str()).fetch_one(&T::get_pool().await?).await;
        if result.is_err() {
            let error = result.err().unwrap();
            return Err(OrmError {
                message: format!("Error trying to execute query {sql_query}: {error}"),
            });
        }
        Ok(result?)
    }

    async fn execute(sql_query: String) -> Result<(), OrmError> {
        let result = sqlx::query(sql_query.as_str()).execute(&T::get_pool().await?).await;
        if result.is_err() {
            let error = result.err().unwrap();
            return Err(OrmError {
                message: format!("Error trying to execute query {sql_query}: {error}"),
            });
        }
        Ok(())
    }
}