package it.integry.integrywmsnative.core.data_store.db.respository_new.data_source;

import androidx.annotation.NonNull;
import androidx.sqlite.db.SimpleSQLiteQuery;

import java.util.List;
import java.util.concurrent.ExecutorService;

import io.reactivex.rxjava3.core.Flowable;
import it.integry.integrywmsnative.core.data_store.db.entity.BaseSyncDTO;
import it.integry.integrywmsnative.core.data_store.db.interfaces.EntityDaoInterface;
import it.integry.integrywmsnative.core.data_store.db.interfaces.EntityModelInterface;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;

public abstract class _BaseRoomDataSource<R extends BaseSyncDTO, T extends EntityDaoInterface<R>> {

    protected final ExecutorService executorService;
    protected final T entityDao;

    protected final String TABLE_NAME;

    public _BaseRoomDataSource(ExecutorService executorService, T entityDao, String tableName) {
        this.executorService = executorService;
        this.entityDao = entityDao;
        this.TABLE_NAME = tableName;
    }


    public void makeRetrieveAllRequest(boolean onlyToBeSync, final RunnableArgs<List<R>> onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                if (onComplete != null)
                    onComplete.run(makeSynchronousRetrieveAllRequest(onlyToBeSync));
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public abstract List<R> makeSynchronousRetrieveAllRequest(boolean onlyToBeSync);

    public abstract Flowable<List<R>> makeSynchronousRetrieveAllLive(boolean onlyToBeSync);


    public void makeInsertAllRequest(final List<R> itemsToInsert, final Runnable onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousInsertAllRequest(itemsToInsert);
                if (onComplete != null) onComplete.run();
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousInsertAllRequest(final List<R> itemsToInsert) {
        if(itemsToInsert == null) return;

        var ids = entityDao.insertAll(itemsToInsert);

        for(int i = 0; i < itemsToInsert.size(); i++) {
            var inputObject = itemsToInsert.get(i);

            if (inputObject instanceof EntityModelInterface) {
                ((EntityModelInterface) inputObject).setId(ids.get(i));
            }
        }
    }


    public void makeInsertRequest(final R itemToInsert, final RunnableArgs<R> onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousInsertRequest(itemToInsert);
                if (onComplete != null) onComplete.run(itemToInsert);
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousInsertRequest(final R itemToInsert) {
        var id = entityDao.insert(itemToInsert);

        if (itemToInsert instanceof EntityModelInterface) {
            ((EntityModelInterface) itemToInsert).setId(id);
        }
    }


    public void makeUpdateRequest(final R itemToUpdate, final RunnableArgs<R> onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousUpdateRequest(itemToUpdate);
                if (onComplete != null) onComplete.run(itemToUpdate);
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousUpdateRequest(final R itemToUpdate) {
        var id = entityDao.update(itemToUpdate);
    }


    public void makeUpdateAllRequest(final List<R> itemToUpdate, final RunnableArgs<List<R>> onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousUpdateAllRequest(itemToUpdate);
                if (onComplete != null) onComplete.run(itemToUpdate);
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousUpdateAllRequest(final List<R> itemsToUpdate) {
        if(itemsToUpdate == null) return;
        entityDao.updateAll(itemsToUpdate);
    }


    public void makeDeleteRequest(@NonNull final R itemToDelete, final Runnable onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousDeleteRequest(itemToDelete);
                if (onComplete != null) onComplete.run();
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousDeleteRequest(@NonNull final R itemToDelete) {
        entityDao.delete(itemToDelete);
    }


    public void makeDeleteAllRequest(@NonNull final List<R> itemsToDelete, final Runnable onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousDeleteAllRequest(itemsToDelete);
                if (onComplete != null) onComplete.run();
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousDeleteAllRequest(@NonNull final List<R> itemsToDelete) {
        for (R itemToDelete : itemsToDelete) {
            makeSynchronousDeleteRequest(itemToDelete);
        }
    }


    public void makeClearAllDataRequest(boolean excludeNotSync, final Runnable onComplete, final RunnableArgs<Exception> onError) {
        executorService.execute(() -> {
            try {
                makeSynchronousClearAllDataRequest(excludeNotSync);
                if (onComplete != null) onComplete.run();
            } catch (Exception ex) {
                if (onError != null) onError.run(ex);
            }
        });
    }

    public void makeSynchronousClearAllDataRequest(boolean excludeNotSync) {
        entityDao
                .execRaw(new SimpleSQLiteQuery("DELETE FROM " + TABLE_NAME + (excludeNotSync ? " WHERE " + BaseSyncDTO.Columns.COLUMN_REMOTE_SYNC_DATE + " IS NOT NULL" : "")));
    }


}
