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

#[async_trait::async_trait]
pub trait CreateRepository where Self: Repository {
    async fn create(entity: Self) -> Result<Self, OrmError>;
    async fn bulk_create(entities: Vec<Self>) -> Result<Vec<Self>, OrmError>;
}

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

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

#[rglw::codegen::test(name="Test Create 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::repository::create_repository::CreateRepository;
    use crate::pagination::{calculate_pagination, Page};
    use crate::repository::read_repository::ReadRepository;
    use crate::types::IdentityType;
    use crate::entity::ForeignKey;
    
    #[derive(Default, Debug, Clone, sqlx::FromRow, Entity)]
    #[connection="create_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 4',
                'user4@rustyglw.org'
            );
            INSERT INTO User(username,email) VALUES (
                'User 5',
                'user5@rustyglw.org'
            );
        ".into()).await;
        assert!(result.is_ok());
    }

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

    pub async fn test_create() {
        let user: User = User {
            username: "New User".to_string(),
            email: "new@test.com".to_string(),
            ..Default::default()
        };
        let created_user = User::create(user).await;
        assert!(created_user.is_ok());
        let id = created_user.unwrap().id;
        let user = User::find_by_id(id).await;
        assert!(user.is_ok());
        let user = user.unwrap();
        assert_eq!(user.id, id);
        assert_eq!(user.username, "New User");
        assert_eq!(user.email, "new@test.com");
    }

    pub async fn test_duplicated() {
        let user: User = User {
            username: "New User".to_string(),
            email: "new@test.com".to_string(),
            ..Default::default()
        };
        let created_user = User::create(user).await;
        assert!(created_user.is_err());
        let error = created_user.unwrap_err();
        assert_eq!(error.message, "error returned from database: (code: 2067) UNIQUE constraint failed: User.email");
    }

    pub async fn test_bulk_insert() {
        let users: Vec<User> = vec![User {
            username: "Bulk User 1".to_string(),
            email: "bu1@test.com".to_string(),
            ..Default::default()
        }, User {
            username: "Bulk User 2".to_string(),
            email: "bu2@test.com".to_string(),
            ..Default::default()
        }];
        let created_users = User::bulk_create(users).await;
        println!("{:?}", created_users);
        assert!(created_users.is_ok());
        let result = created_users.unwrap();
        assert_eq!(result.len(), 2);
        assert_eq!(result.first().unwrap().email, "bu1@test.com");
        assert_eq!(result.last().unwrap().email, "bu2@test.com");
    }
}