package it.integry.integrywmsnative.core.rest.consumers;

import com.annimon.stream.Optional;
import com.annimon.stream.Stream;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Singleton;

import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.model.MtbAart;
import it.integry.integrywmsnative.core.model.MtbDepoPosizione;
import it.integry.integrywmsnative.core.model.MvwSitArtUdcDetInventario;
import it.integry.integrywmsnative.core.rest.RESTBuilder;
import it.integry.integrywmsnative.core.rest.model.ServiceRESTResponse;
import it.integry.integrywmsnative.core.rest.model.giacenza.InstantItemSituationIncomingItemDto;
import it.integry.integrywmsnative.core.rest.model.giacenza.InstantItemSituationResponseDto;
import it.integry.integrywmsnative.core.utility.UtilityString;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

@Singleton
public class GiacenzaRESTConsumer extends _BaseRESTConsumer {

    private final SystemRESTConsumer mSystemRESTConsumer;
    private final ArticoloRESTConsumer mArticoloRESTConsumer;

    public GiacenzaRESTConsumer(SystemRESTConsumer systemRESTConsumer, ArticoloRESTConsumer articoloRESTConsumer) {
        this.mSystemRESTConsumer = systemRESTConsumer;
        this.mArticoloRESTConsumer = articoloRESTConsumer;

    }

    public void getGiacenzeInPosizione(MtbDepoPosizione posizione, RunnableArgs<List<MvwSitArtUdcDetInventario>> onComplete, RunnableArgs<Exception> onFailed) {
        GiacenzaRESTConsumerService giacenzaRESTConsumerService = RESTBuilder.getService(GiacenzaRESTConsumerService.class);
        giacenzaRESTConsumerService.retrieveAvailableItems(posizione.getPosizione()).enqueue(new Callback<>() {
            @Override
            public void onResponse(Call<ServiceRESTResponse<List<MvwSitArtUdcDetInventario>>> call, Response<ServiceRESTResponse<List<MvwSitArtUdcDetInventario>>> response) {
                analyzeAnswer(response, "getGiacenzeInPosizione", inventarioList -> {

                    List<String> codMarts = Stream.of(inventarioList)
                            .map(x -> x.getCodMart().trim())
                            .toList();

                    mArticoloRESTConsumer.getByCodMarts(codMarts, mtbAarts -> {
                        for (var row : inventarioList) {

                            MtbAart foundMtbAart = null;
                            Optional<MtbAart> mtbAartOpt = Stream.of(mtbAarts)
                                    .filter(x -> x.getCodMart().equalsIgnoreCase(row.getCodMart()))
                                    .findFirst();

                            if (mtbAartOpt.isPresent()) {
                                foundMtbAart = mtbAartOpt.get();
                            }

                            row.setMtbAart(foundMtbAart);
                        }

                        onComplete.run(inventarioList);
                    }, onFailed);
                }, onFailed);
            }

            @Override
            public void onFailure(Call<ServiceRESTResponse<List<MvwSitArtUdcDetInventario>>> call, Throwable t) {
                onFailed.run(new Exception(t));
            }
        });
    }

    public void getGiacenzeByArticolo(String codMart, String partitaMag, RunnableArgs<List<MvwSitArtUdcDetInventario>> onComplete, RunnableArgs<Exception> onFailed) {
        GiacenzaRESTConsumerService giacenzaRESTConsumerService = RESTBuilder.getService(GiacenzaRESTConsumerService.class);
        var serviceRESTResponseCall =
                UtilityString.isNullOrEmpty(partitaMag) ?
                        giacenzaRESTConsumerService.retrieveAvailableItemsByArt(codMart) :
                        giacenzaRESTConsumerService.retrieveAvailableItemsByArt(codMart, partitaMag);
        serviceRESTResponseCall.enqueue(new Callback<>() {
            @Override
            public void onResponse(Call<ServiceRESTResponse<List<MvwSitArtUdcDetInventario>>> call, Response<ServiceRESTResponse<List<MvwSitArtUdcDetInventario>>> response) {
                analyzeAnswer(response, "getGiacenzeByArticolo", inventarioList -> {

                    List<String> codMarts = Stream.of(inventarioList)
                            .map(x -> x.getCodMart().trim())
                            .distinct()
                            .toList();

                    mArticoloRESTConsumer.getByCodMarts(codMarts, mtbAarts -> {
                        for (var row : inventarioList) {

                            MtbAart foundMtbAart = null;
                            Optional<MtbAart> mtbAartOpt = Stream.of(mtbAarts)
                                    .filter(x -> x.getCodMart().equalsIgnoreCase(row.getCodMart()))
                                    .findFirst();

                            if (mtbAartOpt.isPresent()) {
                                foundMtbAart = mtbAartOpt.get();
                            }

                            row.setMtbAart(foundMtbAart);
                        }

                        onComplete.run(inventarioList);
                    }, onFailed);
                }, onFailed);
            }

            @Override
            public void onFailure(Call<ServiceRESTResponse<List<MvwSitArtUdcDetInventario>>> call, Throwable t) {
                onFailed.run(new Exception(t));
            }
        });
    }


    public void getInstantItemSituation(String codMdep, String codMart, String partitaMag, String codJcom, RunnableArgs<InstantItemSituationResponseDto> onComplete, RunnableArgs<Exception> onFailed) {
        GiacenzaRESTConsumerService giacenzaRESTConsumerService = RESTBuilder.getService(GiacenzaRESTConsumerService.class);

        giacenzaRESTConsumerService.retrieveInstantItemSituation(codMdep, codMart, partitaMag, codJcom)
                .enqueue(new Callback<>() {
                    @Override
                    public void onResponse(Call<ServiceRESTResponse<InstantItemSituationResponseDto>> call, Response<ServiceRESTResponse<InstantItemSituationResponseDto>> response) {
                        analyzeAnswer(response, "getInstantItemSituation", data -> {

                            fillInstantItemSituationWithMtbAarts(data, onComplete, onFailed);
                        }, onFailed);
                    }

                    @Override
                    public void onFailure(Call<ServiceRESTResponse<InstantItemSituationResponseDto>> call, Throwable t) {
                        onFailed.run(new Exception(t));
                    }
                });
    }

    private void fillInstantItemSituationWithMtbAarts(InstantItemSituationResponseDto data, RunnableArgs<InstantItemSituationResponseDto> onComplete, RunnableArgs<Exception> onFailed) {
        var inputCodMarts = new ArrayList<String>();

        if (data.getIncomingItems() != null && !data.getIncomingItems().isEmpty()) {
            inputCodMarts.addAll(Stream.of(data.getIncomingItems())
                    .map(InstantItemSituationIncomingItemDto::getCodMart)
                    .toList());
        }

        if (data.getAvailableItems() != null && !data.getAvailableItems().isEmpty()) {
            inputCodMarts.addAll(Stream.of(data.getAvailableItems())
                    .map(MvwSitArtUdcDetInventario::getCodMart)
                    .toList());
        }

        if (inputCodMarts.isEmpty()) {
            onComplete.run(data);
            return;
        }

        mArticoloRESTConsumer.getByCodMarts(inputCodMarts, mtbAarts -> {

            mArticoloRESTConsumer.fillMtbAartsWithMtbUntMis(mtbAarts, newMtbAarts -> {
                if (data.getIncomingItems() != null && !data.getIncomingItems().isEmpty()) {
                    for (var item : data.getIncomingItems()) {
                        var mtbAart = Stream.of(newMtbAarts).filter(x -> x.getCodMart().equalsIgnoreCase(item.getCodMart()))
                                .findFirstOrElse(null);

                        if (mtbAart != null)
                            item.setMtbAart(mtbAart);
                    }
                }

                if (data.getAvailableItems() != null && !data.getAvailableItems().isEmpty()) {
                    for (var item : data.getAvailableItems()) {
                        var mtbAart = Stream.of(newMtbAarts).filter(x -> x.getCodMart().equalsIgnoreCase(item.getCodMart()))
                                .findFirstOrElse(null);

                        if (mtbAart != null)
                            item.setMtbAart(mtbAart);
                    }
                }

                onComplete.run(data);
            }, onFailed);

        }, onFailed);
    }


}
