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

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import it.integry.integrywmsnative.core.data_store.db.entity.BaseSyncDTO;
import it.integry.integrywmsnative.core.data_store.db.respository_new.data_source._BaseRoomDataSource;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
import it.integry.integrywmsnative.core.expansion.RunnableArgssWithReturn;
import it.integry.integrywmsnative.core.mapper.BaseMapper;
import it.integry.integrywmsnative.core.model.BaseRestDTO;
import it.integry.integrywmsnative.core.rest.consumers._BaseRESTConsumer;

public abstract class _BaseRepository<
        A extends BaseRestDTO,
        B extends BaseSyncDTO,
        T extends BaseMapper<A, B>,
        U extends _BaseRoomDataSource<B, ?>,
        V extends _BaseRESTConsumer> {

    protected final T dataMapper;
    protected final U localDataSource;
    protected final V remoteDataSource;

    public _BaseRepository(T dataMapper, U localDataSource, V remoteDataSource) {
        this.dataMapper = dataMapper;
        this.localDataSource = localDataSource;
        this.remoteDataSource = remoteDataSource;
    }

    protected void resolveFetch(List<A> remoteData,
                                List<B> localData,
                                RunnableArgssWithReturn<B, B, Boolean> keyComparator,
                                RunnableArgss<B, B> dataCopy,
                                Runnable onComplete,
                                RunnableArgs<Exception> onError) {
        List<B> finalRemoteData = dataMapper.mapRestsToRooms(remoteData);

        if (localData == null) localData = new ArrayList<>();
        List<B> finalLocalData = localData;

        for (var x : finalRemoteData) {
            var matched = finalLocalData.stream().filter(y -> keyComparator.run(y, x))
                    .findFirst()
                    .orElse(null);

            if (matched != null) {
                x.setId(matched.getId());
                dataCopy.run(x, matched);
            }
        }


        var dataToUpdate = finalRemoteData.stream()
                .filter(x -> x.getId() != null && finalLocalData.stream().noneMatch(x::equals))
                .collect(Collectors.toList());

        var dataToDelete = finalLocalData.stream()
                .filter(x -> x.isSyncronized() && finalRemoteData.stream().noneMatch(y -> keyComparator.run(y, x)))
                .collect(Collectors.toList());

        var dataToInsert = finalRemoteData.stream()
                .filter(x -> x.getId() == null)
                .collect(Collectors.toList());


        localDataSource.makeUpdateAllRequest(dataToUpdate, localUpdateResult -> {

            localDataSource.makeDeleteAllRequest(dataToDelete, () -> {

                localDataSource.makeInsertAllRequest(dataToInsert, onComplete, onError);

            }, onError);

        }, onError);
    }

}
