use crate::error::OrmError;
use crate::repository::read_repository::ReadRepository;
use crate::traits::repository::Repository;

#[async_trait::async_trait]
pub trait DeleteRepository where Self: Repository + ReadRepository {
    async fn delete_by_id(entity: Self) -> Result<Self, OrmError>;
    async fn bulk_delete_by_id(entities: Vec<Self>) -> Result<Vec<Self>, OrmError>;
    async fn bulk_delete_by_example(example: Self) -> Result<Vec<Self>, OrmError>;
}

#[async_trait::async_trait]
impl <T> DeleteRepository for T where T: Repository {
    async fn delete_by_id(entity: Self) -> Result<Self, OrmError> {
        let pool = Self::get_pool().await?;
        entity.delete(&pool).await
    }

    async fn bulk_delete_by_id(entities: Vec<Self>) -> Result<Vec<Self>, OrmError> {
        let pool = Self::get_pool().await?;
        Self::bulk_delete(entities, &pool).await
    }

    async fn bulk_delete_by_example(example: Self) -> Result<Vec<Self>, OrmError> {
        let pool = Self::get_pool().await?;
        let entities = example.query_example(None, &pool).await?;
        Self::bulk_delete_by_id(entities.data).await
    }
}

#[rglw::codegen::test(name="Test Delete Repository")]
mod tests {
    use crate::repository::Repository;
    use crate::{error::OrmError, get_connection, traits::entity::Entity};
    use crate::{JoinType, get_pool, pagination::Pagination, query::{Query, SelectQuery, InsertQuery, UpdateQuery, DropQuery, DeleteQuery, WhereStatement, WhereType, WhereType::And, WhereType::Or}};
    use rglw_orm_codegen::{Entity};
    use sqlx::{Any, Pool};
    use std::path::Path;
    use std::{env, fs};
    use crate::pagination::{calculate_pagination, Page};
    use crate::repository::delete_repository::DeleteRepository;
    use crate::repository::read_repository::ReadRepository;
    use crate::types::IdentityType;
    use crate::entity::ForeignKey;
    
    #[derive(Default, Debug, Clone, sqlx::FromRow, Entity)]
    #[connection="delete_repository"]
    struct User {
        #[primary_key]
        pub id: i64,
        pub username: String,
        pub email: String,
    }

    #[rglw_codegen::before_all]
    pub async fn before_all() {
        env::set_var("GLWAPI_CONFIGURATION_LOCATION", "../database.toml");
        let result = User::execute(
            "CREATE TABLE IF NOT EXISTS User(\
                id INTEGER PRIMARY KEY AUTOINCREMENT,\
                username TEXT NOT NULL,
                email TEXT NOT NULL,
                CONSTRAINT Unique_email UNIQUE (email)
            )".into()).await;
        assert!(result.is_ok());
        let result = User::execute(
            "\
            INSERT INTO User(username,email) VALUES (
                'Jonathan',
                'email@rustyglw.org'
            );
            INSERT INTO User(username,email) VALUES (
                'User 1',
                'user1@rustyglw.org'
            );
            INSERT INTO User(username,email) VALUES (
                'User 2',
                'user2@rustyglw.org'
            );
            INSERT INTO User(username,email) VALUES (
                'User 3',
                'user3@rustyglw.org'
            );
            INSERT INTO User(username,email) VALUES (
                'User',
                'user4@rustyglw.org'
            );
            INSERT INTO User(username,email) VALUES (
                'User',
                'user5@rustyglw.org'
            );
        ".into()).await;
        assert!(result.is_ok());
    }

    #[rglw_codegen::after_all]
    pub async fn after_all() {
        fs::remove_file(Path::new("./delete_repository.sqlite")).unwrap();
    }

    pub async fn test_delete_by_id() {
        let user = User::find_by_id(1).await;
        assert!(user.is_ok());
        let user = user.unwrap();
        let deleted_user = User::delete_by_id(user.clone()).await;
        assert!(deleted_user.is_ok());
        let deleted_user = deleted_user.unwrap();
        assert_eq!(user.id, deleted_user.id);
        let user = User::find_by_id(1).await;
        assert!(user.is_err());
    }

    pub async fn test_bulk_delete_by_id() {
        let user1 = User::find_by_id(2).await;
        assert!(user1.is_ok());
        let user1 = user1.unwrap();
        let user2 = User::find_by_id(3).await;
        assert!(user2.is_ok());
        let user2 = user2.unwrap();
        let deleted_users = User::bulk_delete_by_id(vec![user1, user2]).await;
        assert!(deleted_users.is_ok());
        let deleted_users = deleted_users.unwrap();
        assert_eq!(deleted_users.first().unwrap().id, 2);
        assert_eq!(deleted_users.last().unwrap().id, 3);
        let user1 = User::find_by_id(2).await;
        assert!(user1.is_err());
        let user2 = User::find_by_id(3).await;
        assert!(user2.is_err());
    }
    
    pub async fn test_bulk_delete_by_example() {
        let deleted_users = User::bulk_delete_by_example(User {
            username: "User".to_string(),
            ..Default::default()
        }).await;
        assert!(deleted_users.is_ok());
        let deleted_users = deleted_users.unwrap();
        assert_eq!(deleted_users.first().unwrap().id, 5);
        assert_eq!(deleted_users.last().unwrap().id, 6);
        assert_eq!(deleted_users.len(), 2);
        let user1 = User::find_by_id(5).await;
        assert!(user1.is_err());
        let user2 = User::find_by_id(6).await;
        assert!(user2.is_err());
    }
}