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

import com.annimon.stream.Stream;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
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.MtbGrup;
import it.integry.integrywmsnative.core.model.MtbUntMis;
import it.integry.integrywmsnative.core.model.dto.StatoArticoloDTO;
import it.integry.integrywmsnative.core.rest.RESTBuilder;
import it.integry.integrywmsnative.core.rest.model.ServiceRESTResponse;
import it.integry.integrywmsnative.core.rest.model.articolo.RetrieveArticoloByCodMartRequestDTO;
import it.integry.integrywmsnative.core.rest.model.articolo.SearchArticoloByBarcodeRequestDTO;
import it.integry.integrywmsnative.core.rest.model.articolo.SearchArticoloByBarcodeResponseDTO;
import it.integry.integrywmsnative.core.rest.model.articolo.UpdateBarcodeImballoRequestDTO;
import it.integry.integrywmsnative.core.utility.UtilityQuery;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

@Singleton
public class ArticoloRESTConsumer extends _BaseRESTConsumer {

    private final SystemRESTConsumer systemRESTConsumer;

    public ArticoloRESTConsumer(SystemRESTConsumer systemRESTConsumer) {
        this.systemRESTConsumer = systemRESTConsumer;
    }

    public void searchByBarcode(String barcodeProd, RunnableArgs<List<MtbAart>> onComplete, RunnableArgs<Exception> onFailed) {
        ArticoloRESTConsumerService articoloRESTConsumerService = RESTBuilder.getService(ArticoloRESTConsumerService.class);

        var request = new SearchArticoloByBarcodeRequestDTO()
                .setBarcode(barcodeProd)
                .setOnlyActive(true);

        articoloRESTConsumerService
                .searchByBarcode(request)
                .enqueue(new Callback<>() {
                    @Override
                    public void onResponse(Call<ServiceRESTResponse<SearchArticoloByBarcodeResponseDTO>> call, Response<ServiceRESTResponse<SearchArticoloByBarcodeResponseDTO>> response) {
                        analyzeAnswer(response, "searchByBarcode", (m) -> {
                            onComplete.run(response.body().getDto().getArts());
                        }, onFailed);
                    }

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


    public void getByCodMarts(List<String> codMartToFind, RunnableArgs<List<MtbAart>> onComplete, RunnableArgs<Exception> onFailed) {
        getByCodMartsStatic(codMartToFind, onComplete, onFailed);
    }

    public static void getByCodMartsStatic(List<String> codMartToFind, RunnableArgs<List<MtbAart>> onComplete, RunnableArgs<Exception> onFailed) {

        var codMarts = Stream.of(codMartToFind)
                .withoutNulls()
                .distinct().toList();


        ArticoloRESTConsumerService articoloRESTConsumer = RESTBuilder.getService(ArticoloRESTConsumerService.class);
        articoloRESTConsumer
                .getByCodMart(new RetrieveArticoloByCodMartRequestDTO()
                        .setCodMarts(codMarts))
                .enqueue(new Callback<>() {
                    @Override
                    public void onResponse(Call<ServiceRESTResponse<List<MtbAart>>> call, Response<ServiceRESTResponse<List<MtbAart>>> response) {
                        analyzeAnswer(response, "getByCodMart", onComplete, onFailed);
                    }

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

    }

    public void getStatoPartita(String codMart, String partitaMag, RunnableArgs<List<StatoArticoloDTO>> onComplete, RunnableArgs<Exception> onFailed) {

        ArticoloRESTConsumerService articoloRESTConsumer = RESTBuilder.getService(ArticoloRESTConsumerService.class);
        articoloRESTConsumer.getStatoPartita(codMart, partitaMag).enqueue(new Callback<>() {
            @Override
            public void onResponse(Call<ServiceRESTResponse<List<StatoArticoloDTO>>> call, Response<ServiceRESTResponse<List<StatoArticoloDTO>>> response) {
                analyzeAnswer(response, "getStatoPartita", onComplete, onFailed);
            }

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

    }


    public void getByCodMart(String codMartToFind, RunnableArgs<MtbAart> onComplete, RunnableArgs<Exception> onFailed) {

        getByCodMarts(Collections.singletonList(codMartToFind), artList -> {
            onComplete.run(artList != null && !artList.isEmpty() ? artList.get(0) : null);
        }, onFailed);
    }


    public void getArtsGroups(List<String> groupsToFind, RunnableArgs<List<MtbGrup>> onComplete, RunnableArgs<Exception> onFailed) {
        var whereCondMap = Stream.of(groupsToFind)
                .map(x -> {
                    HashMap<String, Object> vars = new HashMap<>();
                    vars.put("cod_mgrp", x);
                    return vars;
                })
                .toList();

        var whereCond = whereCondMap.isEmpty() ? "" : " WHERE " + UtilityQuery.concatFieldListInWhereCond(whereCondMap);

        Type typeOfObjectsList = new TypeToken<ArrayList<MtbGrup>>() {
        }.getType();

        this.systemRESTConsumer.processSql("SELECT * FROM mtb_grup " + whereCond, typeOfObjectsList, onComplete, onFailed);
    }


    public void getUntMisArts(List<String> inputUntMis, RunnableArgs<List<MtbUntMis>> onComplete, RunnableArgs<Exception> onFailed) {
        List<HashMap<String, Object>> whereCondList = Stream.of(inputUntMis)
                .distinct()
                .withoutNulls()
                .map(x -> {
                    HashMap<String, Object> data = new HashMap<>();
                    data.put("unt_mis", x);
                    return data;
                })
                .toList();

        var whereCond = whereCondList.isEmpty() ? "" : " WHERE " + UtilityQuery.concatFieldListInWhereCond(whereCondList);

        Type typeOfObjectsList = new TypeToken<ArrayList<MtbUntMis>>() {
        }.getType();
        this.systemRESTConsumer.processSql("SELECT * FROM mtb_unt_mis " + whereCond, typeOfObjectsList, onComplete, onFailed);
    }

    public void fillMtbAartsWithMtbUntMis(List<MtbAart> inputMtbAart, RunnableArgs<List<MtbAart>> onComplete, RunnableArgs<Exception> onFailed) {
        var inputUntMis = new ArrayList<String>();

        if (inputMtbAart != null && !inputMtbAart.isEmpty()) {
            inputUntMis.addAll(Stream.of(inputMtbAart)
                    .map(MtbAart::getUntMis)
                    .withoutNulls()
                    .distinct()
                    .toList());
        }

        if (inputUntMis.isEmpty()) {
            onComplete.run(inputMtbAart);
            return;
        }

        getUntMisArts(inputUntMis, mtbUntMiss -> {
            for (var item : inputMtbAart) {
                var mtbUntMis = Stream.of(mtbUntMiss).filter(x -> x.getUntMis().equalsIgnoreCase(item.getUntMis()))
                        .findFirstOrElse(null);

                if (mtbUntMis != null)
                    item.setMtbUntMis(Collections.singletonList(mtbUntMis));
            }

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


    public void updateBarcodeImballo(String codMart, String newBarcodeImballo, Runnable onComplete, RunnableArgs<Exception> onFailed) {
        ArticoloRESTConsumerService articoloRESTConsumerService = RESTBuilder.getService(ArticoloRESTConsumerService.class);

        var request = new UpdateBarcodeImballoRequestDTO()
                .setCodMart(codMart)
                .setNewBarcodeImballo(newBarcodeImballo);

        articoloRESTConsumerService
                .updateBarcodeImballo(request)
                .enqueue(new Callback<>() {
                    @Override
                    public void onResponse(Call<ServiceRESTResponse<Void>> call, Response<ServiceRESTResponse<Void>> response) {
                        analyzeAnswer(response, "updateBarcodeImballo", m -> {
                            onComplete.run();
                        }, onFailed);
                    }

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