package it.integry.integrywmsnative.gest.picking_libero;

import androidx.databinding.ObservableArrayList;

import com.annimon.stream.Stream;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;

import it.integry.barcode_base_android_library.model.BarcodeScanDTO;
import it.integry.integrywmsnative.core.exception.NoLUFoundException;
import it.integry.integrywmsnative.core.exception.NoResultFromBarcodeException;
import it.integry.integrywmsnative.core.exception.TooManyLUFoundInMonoLUPositionException;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
import it.integry.integrywmsnative.core.interfaces.viewmodel_listeners.ILUBaseOperationsListener;
import it.integry.integrywmsnative.core.interfaces.viewmodel_listeners.ILoadingListener;
import it.integry.integrywmsnative.core.model.CommonModelConsts;
import it.integry.integrywmsnative.core.model.MtbAart;
import it.integry.integrywmsnative.core.model.MtbColr;
import it.integry.integrywmsnative.core.model.MtbColt;
import it.integry.integrywmsnative.core.model.MtbDepoPosizione;
import it.integry.integrywmsnative.core.model.VtbDest;
import it.integry.integrywmsnative.core.model.dto.PickDataDTO;
import it.integry.integrywmsnative.core.model.secondary.GestioneEnum;
import it.integry.integrywmsnative.core.rest.consumers.ArticoloRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.BarcodeRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.ColliMagazzinoRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.PosizioniRESTConsumer;
import it.integry.integrywmsnative.core.rest.model.Ean128Model;
import it.integry.integrywmsnative.core.rest.model.Ean13PesoModel;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityBarcode;
import it.integry.integrywmsnative.core.utility.UtilityBigDecimal;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityPosizione;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.gest.spedizione.exceptions.InvalidPesoKGException;
import it.integry.integrywmsnative.gest.spedizione.model.PickedQuantityDTO;
import it.integry.integrywmsnative.gest.spedizione.model.PickingObjectDTO;

public class PickingLiberoViewModel {


    private MtbColt mCurrentMtbColt = null;

    private final ArticoloRESTConsumer mArticoloRESTConsumer;
    private final ColliMagazzinoRESTConsumer mColliMagazzinoRESTConsumer;
    private final BarcodeRESTConsumer mBarcodeRESTConsumer;
    private final PosizioniRESTConsumer mPosizioniRESTConsumer;


    private boolean mFlagAskCliente;
    private GestioneEnum mDefaultGestione;


    private Listener mListener;


    @Inject
    public PickingLiberoViewModel(ArticoloRESTConsumer articoloRESTConsumer,
                                  ColliMagazzinoRESTConsumer colliMagazzinoRESTConsumer,
                                  BarcodeRESTConsumer barcodeRESTConsumer,
                                  PosizioniRESTConsumer posizioniRESTConsumer
    ) {
        this.mArticoloRESTConsumer = articoloRESTConsumer;
        this.mColliMagazzinoRESTConsumer = colliMagazzinoRESTConsumer;
        this.mBarcodeRESTConsumer = barcodeRESTConsumer;
        this.mPosizioniRESTConsumer = posizioniRESTConsumer;
    }


    public void init(boolean flagAskCliente, GestioneEnum defaultGestione) {
        this.mFlagAskCliente = defaultGestione == GestioneEnum.VENDITA && flagAskCliente;
        this.mDefaultGestione = defaultGestione;
    }


    public void processBarcodeDTO(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        if (this.mCurrentMtbColt == null) {
            this.createNewLU(null, null, () -> {
                executeEtichettaBehaviour(barcodeScanDTO, onComplete);
            });
        } else {
            executeEtichettaBehaviour(barcodeScanDTO, onComplete);
        }
    }

    private void executeEtichettaBehaviour(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        if (UtilityBarcode.isEtichettaAnonima(barcodeScanDTO)) {
            //Cerco gli articoli presenti nell'ul dell'etichetta anonima
            this.executeEtichettaLU(barcodeScanDTO.getStringValue(), onComplete);

        } else if (UtilityBarcode.isEtichettaPosizione(barcodeScanDTO)) {
            //Cerco tramite etichetta un collo in posizione mono ul
            this.executeEtichettaPosizione(barcodeScanDTO, onComplete);

        } else if (UtilityBarcode.isEtichetta128(barcodeScanDTO)) {
            //Cerco tramite etichetta ean 128 (che può indicarmi una UL)
            this.executeEtichettaEan128(barcodeScanDTO, onComplete);

        } else if (UtilityBarcode.isEanPeso(barcodeScanDTO)) {
            //Cerco tramite etichetta ean 128 (che può indicarmi un articolo o una UL)
            this.executeEtichettaEanPeso(barcodeScanDTO, onComplete);

        } else {
            //Cerco tramite ean13  un collo  in posizione mono ul definita in anagrafica articolo, altrimenti se abilitato procedo con picking manuale
            this.loadArticolo(barcodeScanDTO.getStringValue(), null, onComplete);
        }
    }


    private void executeEtichettaPosizione(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {

        MtbDepoPosizione foundPosizione = Stream.of(SettingsManager.iDB().getAvailablePosizioni())
                .filter(x -> x.getPosizione().equalsIgnoreCase(barcodeScanDTO.getStringValue()))
                .single();
        this.executePosizione(foundPosizione, null, onComplete);

    }

    private void executePosizione(MtbDepoPosizione posizione, MtbAart articolo, Runnable onComplete) {
        this.mPosizioniRESTConsumer.getBancaliInPosizione(posizione, mtbColtList -> {

            if (mtbColtList == null || mtbColtList.size() == 0) {
                if (articolo != null) {
                    this.dispatchArt(articolo, null);
                } else {
                    this.sendError(new NoLUFoundException());
                }
            } else if (mtbColtList.size() == 1) {
                this.mColliMagazzinoRESTConsumer.getByTestata(mtbColtList.get(0), true, false, mtbColt -> {
                    pickMerceULtoUL(mtbColt, articolo, onComplete);
                }, this::sendError);
            } else {
                this.sendError(new TooManyLUFoundInMonoLUPositionException());
            }

        }, this::sendError);
    }

    private void executeEtichettaEanPeso(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        try {
            Ean13PesoModel ean13PesoModel = Ean13PesoModel.fromBarcode(barcodeScanDTO.getStringValue());
            this.loadArticolo(ean13PesoModel.getPrecode(), ean13PesoModel.toEan128(), onComplete);
        } catch (Exception ex) {
            this.sendError(ex);
        }
    }

    private void executeEtichettaLU(String sscc, Runnable onComplete) {
        mColliMagazzinoRESTConsumer.getBySSCC(sscc, true, false, mtbColtScanned -> {

            if (mtbColtScanned == null) {
                this.sendError(new NoLUFoundException());
            } else if ((mtbColtScanned.getGestioneEnum() == GestioneEnum.ACQUISTO || mtbColtScanned.getGestioneEnum() == GestioneEnum.LAVORAZIONE) && mtbColtScanned.getSegno() > 0) {
                pickMerceULtoUL(mtbColtScanned, onComplete);
            } else {
                this.sendError(new NoLUFoundException());
            }

        }, this::sendError);
    }

    private void executeEtichettaEan128(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        mBarcodeRESTConsumer.decodeEan128(barcodeScanDTO, ean128Model -> {

            String barcodeProd = null;

            if (!UtilityString.isNullOrEmpty(ean128Model.Sscc)) barcodeProd = ean128Model.Sscc;
            if (!UtilityString.isNullOrEmpty(ean128Model.Gtin)) barcodeProd = ean128Model.Gtin;
            if (!UtilityString.isNullOrEmpty(ean128Model.Content))
                barcodeProd = ean128Model.Content;


            if (!UtilityString.isNullOrEmpty(barcodeProd)) {

                if (!UtilityString.isNullOrEmpty(ean128Model.Sscc)) {
                    this.executeEtichettaLU(ean128Model.Sscc, onComplete);
                } else if (!UtilityString.isNullOrEmpty(barcodeProd)) {
                    if (barcodeProd.startsWith("0") || barcodeProd.startsWith("9")) {
                        barcodeProd = barcodeProd.substring(1);
                    }
                    this.loadArticolo(barcodeProd, ean128Model, onComplete);
                } else {
                    this.sendError(new NoLUFoundException());
                }


            } else {
                //EAN 128 non completo o comunque mancano i riferimenti al prodotto
                this.sendError(new NoLUFoundException());
            }
        }, this::sendError);
    }

    private void loadArticolo(String barcodeProd, Ean128Model ean128Model, Runnable onComplete) {
        if (barcodeProd.length() == 14) {
//            barcodeProd = UtilityBarcode.convertITF14toNeutral(barcodeProd);
            barcodeProd = UtilityBarcode.convertITF14toEAN13(barcodeProd);
        }

        String finalBarcodeProd = barcodeProd;
        mArticoloRESTConsumer.getByBarcodeProd(barcodeProd, mtbAartList -> {
            if (mtbAartList != null && mtbAartList.size() > 0) {
                MtbAart articolo = mtbAartList.get(0);
                MtbDepoPosizione posizione = UtilityPosizione.getFromCache(articolo.getPosizione());

                if (posizione != null && posizione.isFlagMonoCollo()) {
                    this.executePosizione(posizione, articolo, onComplete);
                } else if (mDefaultGestione == GestioneEnum.VENDITA) {
                    this.dispatchArt(articolo, ean128Model);
                    onComplete.run();
                } else {
                    this.sendError(new NoResultFromBarcodeException(finalBarcodeProd));
                }

            } else {
                this.sendError(new NoResultFromBarcodeException(finalBarcodeProd));
            }

        }, this::sendError);
    }


    public void createNewLU(Integer customNumCollo, String customSerCollo, Runnable onComplete) {
        if (this.mFlagAskCliente) {
            this.sendLUClienteRequired((vtbDest, codJcom) -> {
                createNewLU_PostClienteAsk(customNumCollo, customSerCollo, vtbDest, codJcom, onComplete);
            }, () -> {

            });
        } else {
            createNewLU_PostClienteAsk(customNumCollo, customSerCollo, null, null, onComplete);
        }
    }

    private void createNewLU_PostClienteAsk(Integer customNumCollo, String customSerCollo, VtbDest vtbDest, String codJcom, Runnable onComplete) {
        this.sendOnLoadingStarted();

        MtbColt mtbColt = new MtbColt();
        mtbColt.initDefaultFields(mDefaultGestione)
                .setRifOrd(mDefaultGestione == GestioneEnum.LAVORAZIONE ? "PICKING LIBERO" : null)
//                .setCodAnag(SettingsManager.iDB().getProduzioneDefaultCodAnag())
                .setSegno(-1)
                .setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

        if (customNumCollo != null) {
            mtbColt.setNumCollo(customNumCollo);
        }

        if (!UtilityString.isNullOrEmpty(customSerCollo)) {
            mtbColt.setSerCollo(customSerCollo);
        }

        if (vtbDest != null) {
            mtbColt.setCodAnag(vtbDest.getCodAnag());
            mtbColt.setCodVdes(vtbDest.getCodVdes());
        }

        if (!UtilityString.isNullOrEmpty(codJcom)) {
            mtbColt.setCodJcom(codJcom);
        }

        mColliMagazzinoRESTConsumer.saveCollo(mtbColt, value -> {
            mtbColt
                    .setNumCollo(value.getNumCollo())
                    .setDataCollo(value.getDataColloS())
                    .setMtbColr(new ObservableArrayList<>());

            this.mCurrentMtbColt = mtbColt;

            this.sendOnLoadingEnded();

            if (onComplete != null) onComplete.run();
            this.sendLUOpened(mtbColt);
        }, this::sendError);
    }

    private void dispatchArt(MtbAart mtbAart, Ean128Model ean128Model) {
        PickingObjectDTO pickingObjectDTO = new PickingObjectDTO()
                .setMtbAart(mtbAart)
                .setTempPickData(PickDataDTO.fromEan128(ean128Model));

        BigDecimal numCnfDaPrelevare = null;
        BigDecimal qtaColDaPrelevare = null;
        BigDecimal qtaCnfDaPrelevare = null;

        BigDecimal initialNumCnf;
        BigDecimal initialQtaCnf;
        BigDecimal initialQtaTot;

        String partitaMag = null;
        Date dataScad = null;

        if (pickingObjectDTO.getTempPickData() != null && pickingObjectDTO.getTempPickData().getManualPickDTO() != null) {
            //Oppure le info del barcode scansionato
            PickDataDTO.ManualPickDTO manualPickDTO = pickingObjectDTO.getTempPickData().getManualPickDTO();

            qtaColDaPrelevare = manualPickDTO.getQtaTot();
            numCnfDaPrelevare = manualPickDTO.getNumCnf();

            if (mtbAart.isFlagQtaCnfFissaBoolean()) {
                qtaCnfDaPrelevare = mtbAart.getQtaCnf();

                if (manualPickDTO.isEanPeso()) {
                    if (mtbAart.getUntMisRifPeso() == MtbAart.UntMisRifPesoEnum.M) {
                        if (UtilityBigDecimal.equalsTo(mtbAart.getPesoKg(), BigDecimal.ZERO)) {
                            this.sendError(new InvalidPesoKGException());
                        } else {
                            qtaColDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, mtbAart.getPesoKg());
                        }
                    }
                }

            } else if (qtaColDaPrelevare != null && numCnfDaPrelevare != null) {
                qtaCnfDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, numCnfDaPrelevare);
            }


            if (qtaColDaPrelevare != null && numCnfDaPrelevare == null) {
                if (!mtbAart.isFlagQtaCnfFissaBoolean()) {
                    numCnfDaPrelevare = UtilityBigDecimal.divideAndRoundToInteger(qtaColDaPrelevare, mtbAart.getQtaCnf());
                    if (UtilityBigDecimal.equalsTo(numCnfDaPrelevare, BigDecimal.ZERO))
                        numCnfDaPrelevare = BigDecimal.ONE;
                    qtaCnfDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, numCnfDaPrelevare);
                } else {
                    numCnfDaPrelevare = UtilityBigDecimal.divideToInteger(qtaColDaPrelevare, mtbAart.getQtaCnf());
                }

            } else if (numCnfDaPrelevare != null && qtaColDaPrelevare == null) {
                qtaCnfDaPrelevare = mtbAart.getQtaCnf();
                qtaColDaPrelevare = UtilityBigDecimal.multiply(numCnfDaPrelevare, qtaCnfDaPrelevare);
            }

            if (manualPickDTO.getMtbPartitaMag() != null) {
                partitaMag = manualPickDTO.getMtbPartitaMag().getPartitaMag();
                dataScad = manualPickDTO.getMtbPartitaMag().getDataScadD();
            }

            initialNumCnf = numCnfDaPrelevare;
            initialQtaCnf = qtaCnfDaPrelevare;
            initialQtaTot = qtaColDaPrelevare;
        } else {
            initialNumCnf = BigDecimal.ONE;
            initialQtaCnf = mtbAart.getQtaCnf();
            initialQtaTot = UtilityBigDecimal.multiply(initialNumCnf, initialQtaCnf);
        }

        if (numCnfDaPrelevare == null && qtaColDaPrelevare == null && initialNumCnf == null && initialQtaTot == null) {
            initialNumCnf = BigDecimal.ONE;
            initialQtaCnf = mtbAart.getQtaCnf();
            initialQtaTot = UtilityBigDecimal.multiply(initialNumCnf, initialQtaCnf);
        }


        this.sendOnItemDispatched(
                pickingObjectDTO,
                pickingObjectDTO.getMtbAart(),
                initialNumCnf,
                initialQtaCnf,
                initialQtaTot,
                null,
                null,
                null,
                null,
                null,
                null,
                partitaMag,
                dataScad,
                false,
                true,
                true,
                (pickedQuantityDTO, shouldCloseLU) -> {
                    this.saveNewRow(pickingObjectDTO,
                            pickedQuantityDTO.getNumCnf(),
                            pickedQuantityDTO.getQtaCnf(),
                            pickedQuantityDTO.getQtaTot(),
                            pickedQuantityDTO.getPartitaMag(),
                            pickedQuantityDTO.getDataScad(),
                            shouldCloseLU);
                });
    }

    private void pickMerceULtoUL(MtbColt sourceMtbColt, Runnable onComplete) {
        pickMerceULtoUL(sourceMtbColt, null, onComplete);
    }

    private void pickMerceULtoUL(MtbColt sourceMtbColt, MtbAart mtbAart, Runnable onComplete) {
        List<MtbColr> mtbColrsToPick = Stream.of(sourceMtbColt.getMtbColr())
                .filter(x -> UtilityBigDecimal.greaterThan(x.getQtaCol(), BigDecimal.ZERO))
                .toList();

        this.sendArtSelectionRequest(mtbColrsToPick, mtbAart, pickedAarts -> {
            List<MtbColr> destNewMtbColr = new ArrayList<>();

            if (pickedAarts.size() > 0) {
                askQuantities(pickedAarts.iterator(), destNewMtbColr, () -> {
                    if (destNewMtbColr.size() > 0) {
                        MtbColt clonedTestata = (MtbColt) this.mCurrentMtbColt.clone();
                        clonedTestata.setOperation(CommonModelConsts.OPERATION.UPDATE);

                        ObservableArrayList<MtbColr> mtbColrObservableField = new ObservableArrayList<>();

                        for (int i = 0; i < destNewMtbColr.size(); i++) {
                            MtbColr cloneMtbColr = (MtbColr) destNewMtbColr.get(i).clone();

                            cloneMtbColr.setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

                            cloneMtbColr
                                    .setGestioneRif(cloneMtbColr.getGestione())
                                    .setGestione((String) null)

                                    .setSerColloRif(cloneMtbColr.getSerCollo())
                                    .setSerCollo(null)

                                    .setNumColloRif(cloneMtbColr.getNumCollo())
                                    .setNumCollo(null)

                                    .setDataColloRif(cloneMtbColr.getDataColloS())
                                    .setDataCollo((String) null)

                                    .setPesoLordoKg(null)
                                    .setPesoNettoKg(null);

                            mtbColrObservableField.add(cloneMtbColr);
                        }

                        clonedTestata.setMtbColr(mtbColrObservableField);

                        this.sendOnLoadingStarted();

                        new Thread(() -> {
                            mColliMagazzinoRESTConsumer.saveCollo(clonedTestata, value -> {
                                for (int i = 0; i < mtbColrObservableField.size(); i++) {
                                    MtbColr initialMtbColr = mtbColrObservableField.get(i);
                                    var startIdx = (value.getMtbColr().size() - mtbColrObservableField.size());

                                    initialMtbColr
                                            .setDataCollo(value.getDataColloS())
                                            .setNumCollo(value.getNumCollo())
                                            .setGestione(value.getGestione())
                                            .setSerCollo(value.getSerCollo());

                                    initialMtbColr.setRiga(value.getMtbColr().get(startIdx + i).getRiga());
                                    mCurrentMtbColt.getMtbColr().add(mtbColrObservableField.get(i));
                                }

                                this.sendOnRowSaved();
                                this.sendOnLoadingEnded();

                                onComplete.run();

                            }, this::sendError);

                        }).start();
                    }
                }, onComplete);
            } else {
                onComplete.run();
            }
        }, this::sendOnLoadingEnded);

    }

    private void askQuantities(Iterator<MtbColr> sourceMtbColrs, List<MtbColr> destMtbColr, Runnable onComplete, Runnable onAbort) {
        if (sourceMtbColrs.hasNext()) {
            askSingleQuantity(sourceMtbColrs.next(), mtbColr -> {
                destMtbColr.add(mtbColr);
                askQuantities(sourceMtbColrs, destMtbColr, onComplete, onAbort);
            }, onAbort);
        } else {
            onComplete.run();
        }
    }

    private void askSingleQuantity(MtbColr mtbColr, RunnableArgs<MtbColr> onComplete, Runnable onAbort) {

        MtbColt sourceMtbColt = new MtbColt()
                .setNumCollo(mtbColr.getNumCollo())
                .setGestione(mtbColr.getGestione())
                .setDataCollo(mtbColr.getDataColloD())
                .setSerCollo(mtbColr.getSerCollo())
                .setMtbColr(new ObservableArrayList<>());

        sourceMtbColt
                .getMtbColr()
                .add(mtbColr);

        PickingObjectDTO pickingObjectDTO = new PickingObjectDTO()
                .setMtbAart(mtbColr.getMtbAart())
                .setTempPickData(
                        new PickDataDTO()
                                .setSourceMtbColt(sourceMtbColt));


        this.sendOnItemDispatched(
                pickingObjectDTO,
                pickingObjectDTO.getMtbAart(),
                mtbColr.getNumCnf(),
                mtbColr.getQtaCnf(),
                mtbColr.getQtaCol(),
                null,
                null,
                null,
                null,
                null,
                null,
                mtbColr.getPartitaMag(),
                mtbColr.getDataScadPartitaD(),
                false,
                false,
                false,
                (pickedQuantityDTO, shouldCloseLU) -> {

                    mtbColr
                            .setPartitaMag(pickedQuantityDTO.getPartitaMag())
                            .setDataScadPartita(pickedQuantityDTO.getDataScad())
                            .setQtaCol(pickedQuantityDTO.getQtaTot())
                            .setQtaCnf(pickedQuantityDTO.getQtaCnf())
                            .setNumCnf(pickedQuantityDTO.getNumCnf())
                            .setDatetimeRow(UtilityDate.getDateInstance());

                    onComplete.run(mtbColr);
                });
    }


    public void saveNewRow(PickingObjectDTO pickingObjectDTO, BigDecimal numCnf, BigDecimal qtaCnf, BigDecimal qtaTot, String partitaMag, Date dataScad, boolean shouldCloseLU) {
        new Thread(this::sendOnLoadingStarted).start();


        final MtbColr mtbColr = new MtbColr()
                .setCodMart(pickingObjectDTO.getMtbAart().getCodMart())
                .setPartitaMag(partitaMag)
                .setDataScadPartita(dataScad)
                .setQtaCol(qtaTot)
                .setQtaCnf(qtaCnf)
                .setNumCnf(numCnf)
                .setDescrizione(pickingObjectDTO.getMtbAart().getDescrizioneEstesa())
                .setDatetimeRow(UtilityDate.getDateInstance());

        mtbColr.setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

        MtbColt cloneMtbColt = (MtbColt) mCurrentMtbColt.clone();
        cloneMtbColt.setOperation(CommonModelConsts.OPERATION.UPDATE);

        cloneMtbColt.setMtbColr(new ObservableArrayList<>());
        cloneMtbColt.getMtbColr().add((MtbColr) mtbColr.clone());

        if (UtilityBigDecimal.equalsTo(numCnf, BigDecimal.ZERO) && UtilityBigDecimal.equalsTo(qtaTot, BigDecimal.ZERO)) {
            this.sendOnLoadingEnded();
            return;
        }

        mColliMagazzinoRESTConsumer.saveCollo(cloneMtbColt, value -> {
            mtbColr
                    .setDataCollo(value.getDataColloS())
                    .setNumCollo(value.getNumCollo())
                    .setGestione(value.getGestione())
                    .setSerCollo(value.getSerCollo())
                    .setRiga(value.getMtbColr().get(value.getMtbColr().size() - 1).getRiga())
                    .setUntMis(pickingObjectDTO.getMtbAart().getUntMis())
                    .setMtbAart(pickingObjectDTO.getMtbAart());

            mCurrentMtbColt.getMtbColr().add(mtbColr);

            this.sendOnRowSaved();
            this.sendOnLoadingEnded();

            if (shouldCloseLU) closeLU(null);
        }, this::sendError);

    }


    private void loadRifULFromMtbColr(MtbColr mtbColr, RunnableArgs<MtbColt> onComplete) {
        //Se ho dei riferimenti ad una UL devo leggere la QTA ancora disponibile sulla Ul
        if (mtbColr != null &&
                !UtilityString.isNullOrEmpty(mtbColr.getGestioneRif()) &&
                !UtilityString.isNullOrEmpty(mtbColr.getSerColloRif()) &&
                !UtilityString.isNullOrEmpty(mtbColr.getDataColloRifS()) &&
                mtbColr.getNumColloRif() != null) {

            mColliMagazzinoRESTConsumer.getByChiaveCollo(
                    mtbColr.getGestioneRifEnum(),
                    mtbColr.getNumColloRif(),
                    mtbColr.getDataColloRifS(),
                    mtbColr.getSerColloRif(),
                    true,
                    false,
                    onComplete,
                    this::sendError);

        } else {
            onComplete.run(null);
        }
    }


    public void dispatchRowEdit(MtbColr mtbColrToUpdate) {

        final PickingObjectDTO pickingObjectDTO = new PickingObjectDTO()
                .setMtbAart(mtbColrToUpdate.getMtbAart());

        loadRifULFromMtbColr(mtbColrToUpdate, mtbColtRif -> {


            BigDecimal totalQtaAvailable = null;
            BigDecimal totalNumCnfAvailable = null;
            BigDecimal qtaCnfAvailable = null;

            List<MtbColr> mtbColrRifs = mtbColtRif != null &&
                    mtbColtRif.getMtbColr() != null ? mtbColtRif.getMtbColr() : null;

            MtbColr mtbColrRif = null;

            if (mtbColrRifs != null && mtbColrRifs.size() > 0) {
                //TODO: Da capire se è necessario controllare anche il cod_jcom
                mtbColrRif = Stream.of(mtbColrRifs)
                        .filter(x -> UtilityString.equalsIgnoreCase(x.getCodMart(), mtbColrToUpdate.getCodMart()) &&
                                UtilityString.equalsIgnoreCase(x.getCodCol(), mtbColrToUpdate.getCodCol()) &&
                                UtilityString.equalsIgnoreCase(x.getCodTagl(), mtbColrToUpdate.getCodTagl()) &&
                                UtilityString.equalsIgnoreCase(x.getPartitaMag(), mtbColrToUpdate.getPartitaMag()))
                        .findFirstOrElse(null);
            }


            if (mtbColrRif != null) {

                totalQtaAvailable = mtbColrRif.getQtaCol().add(mtbColrToUpdate.getQtaCol());
                totalNumCnfAvailable = mtbColrRif.getNumCnf().add(mtbColrToUpdate.getNumCnf());
                qtaCnfAvailable = mtbColrRif.getQtaCnf();

            } else {

                totalQtaAvailable = mtbColrToUpdate.getQtaCol();
                totalNumCnfAvailable = mtbColrToUpdate.getNumCnf();
                qtaCnfAvailable = mtbColrToUpdate.getQtaCnf();

            }

            this.sendOnItemDispatched(
                    pickingObjectDTO,
                    pickingObjectDTO.getMtbAart(),
                    mtbColrToUpdate.getNumCnf(),
                    mtbColrToUpdate.getQtaCnf(),
                    mtbColrToUpdate.getQtaCol(),
                    totalQtaAvailable,
                    totalNumCnfAvailable,
                    qtaCnfAvailable,
                    null,
                    null,
                    null,
                    mtbColrToUpdate.getPartitaMag(),
                    mtbColrToUpdate.getDataScadPartitaD(),
                    false,
                    false,
                    true,
                    (pickedQuantityDTO, shouldCloseLU) -> {

                        this.saveEditedRow(mtbColrToUpdate,
                                pickedQuantityDTO.getNumCnf(),
                                pickedQuantityDTO.getQtaCnf(),
                                pickedQuantityDTO.getQtaTot(),
                                pickedQuantityDTO.getPartitaMag(),
                                pickedQuantityDTO.getDataScad(),
                                shouldCloseLU);
                    });


        });
    }


    private void saveEditedRow(MtbColr mtbColrToUpdate, BigDecimal numCnf, BigDecimal qtaCnf, BigDecimal qtaTot, String partitaMag, Date dataScad, boolean shouldCloseLU) {

        this.sendOnLoadingStarted();


        MtbColt mtbColt = new MtbColt()
                .setNumCollo(mtbColrToUpdate.getNumCollo())
                .setDataCollo(mtbColrToUpdate.getDataColloS())
                .setSerCollo(mtbColrToUpdate.getSerCollo())
                .setGestione(mtbColrToUpdate.getGestione())
                .setMtbColr(new ObservableArrayList<>());
        mtbColt.setOperation(CommonModelConsts.OPERATION.NO_OP);

        final MtbColr mtbColr = (MtbColr) mtbColrToUpdate.clone();
        mtbColr.setOperation(CommonModelConsts.OPERATION.UPDATE);
        mtbColr
                .setNumCnf(numCnf)
                .setQtaCnf(qtaCnf)
                .setQtaCol(qtaTot)
                .setPartitaMag(partitaMag)
                .setDataScadPartita(dataScad)
                .setDatetimeRow(UtilityDate.getDateInstance());

        mtbColt.getMtbColr().add(mtbColr);

        this.mColliMagazzinoRESTConsumer.saveCollo(mtbColt, (value) -> {

            mtbColr.setNumCnf(numCnf)
                    .setQtaCnf(qtaCnf)
                    .setQtaCol(qtaTot);

            this.mCurrentMtbColt.getMtbColr().remove(mtbColrToUpdate);
            this.mCurrentMtbColt.getMtbColr().add(mtbColr);

            this.sendOnRowSaved();
            this.sendOnLoadingEnded();

        }, this::sendError);
    }


    public void deleteRow(MtbColr mtbColrToDelete) {
        this.sendMtbColrDeleteRequest(shouldDelete -> {
            if (shouldDelete) {
                this.sendOnLoadingStarted();

                MtbColt mtbColt = new MtbColt()
                        .setNumCollo(mtbColrToDelete.getNumCollo())
                        .setDataCollo(mtbColrToDelete.getDataColloS())
                        .setSerCollo(mtbColrToDelete.getSerCollo())
                        .setGestione(mtbColrToDelete.getGestione())
                        .setMtbColr(new ObservableArrayList<>());
                mtbColt.setOperation(CommonModelConsts.OPERATION.NO_OP);

                MtbColr mtbColr = (MtbColr) mtbColrToDelete.clone();

                mtbColr.setOperation(CommonModelConsts.OPERATION.DELETE);
                mtbColt.getMtbColr().add(mtbColr);

                this.mColliMagazzinoRESTConsumer.saveCollo(mtbColt, (value) -> {
                    this.mCurrentMtbColt.getMtbColr().remove(mtbColrToDelete);

                    this.sendOnRowSaved();
                    this.sendOnLoadingEnded();

                }, this::sendError);
            }
        });
    }


    public void closeLU(Runnable onComplete) {
        if (mCurrentMtbColt == null) {
            if (onComplete != null) onComplete.run();
            return;
        }

        this.sendOnLoadingStarted();

        mColliMagazzinoRESTConsumer.canLUBeDeleted(mCurrentMtbColt, canBeDeleted -> {
            if (canBeDeleted) {
                deleteLU(() -> {
                    this.sendLUClosed();
                    this.sendOnLoadingEnded();

                    this.mCurrentMtbColt = null;
                    if (onComplete != null) onComplete.run();
                });
            } else {

                this.mColliMagazzinoRESTConsumer.assegnaLottoSuColloScarico(mCurrentMtbColt, () -> {
                    this.mColliMagazzinoRESTConsumer.updateDataFine(mCurrentMtbColt, () -> {
                        this.sendLUClosed();
                        this.sendOnLoadingEnded();

                        this.mCurrentMtbColt = null;
                        if (onComplete != null) onComplete.run();
                    }, this::sendError);
                }, this::sendError);
            }
        }, this::sendError);
    }

    private void deleteLU(Runnable onComplete) {
        mColliMagazzinoRESTConsumer.deleteCollo(mCurrentMtbColt, () -> {
            this.mCurrentMtbColt = null;

            if (onComplete != null) onComplete.run();
        }, this::sendError);
    }


    private void sendOnLoadingStarted() {
        if (this.mListener != null) mListener.onLoadingStarted();
    }

    private void sendOnLoadingEnded() {
        if (this.mListener != null) mListener.onLoadingEnded();
    }

    private void sendError(Exception ex) {
        if (this.mListener != null) mListener.onError(ex);
    }

    private void sendLUOpened(MtbColt mtbColt) {
        if (this.mListener != null) mListener.onLUOpened(mtbColt);
    }

    private void sendLUClosed() {
        if (this.mListener != null) mListener.onLUClosed();
    }

    private void sendMtbColrDeleteRequest(RunnableArgs<Boolean> onComplete) {
        if (this.mListener != null) mListener.onMtbColrDeleteRequest(onComplete);
    }

    private void sendLUClienteRequired(RunnableArgss<VtbDest, String> onComplete, Runnable onAbort) {
        if (this.mListener != null) mListener.onLUClienteRequired(onComplete, onAbort);
    }

    private void sendArtSelectionRequest(List<MtbColr> mtbColrsToPick, MtbAart mtbAart, RunnableArgs<List<MtbColr>> onComplete, Runnable onAbort) {
        if (this.mListener != null)
            mListener.onArtSelectionRequest(mtbColrsToPick, mtbAart, onComplete, onAbort);
    }

    private void sendOnItemDispatched(PickingObjectDTO pickingObjectDTO,
                                      MtbAart mtbAart,
                                      BigDecimal initialNumCnf,
                                      BigDecimal initialQtaCnf,
                                      BigDecimal initialQtaTot,
                                      BigDecimal totalQtaAvailable,
                                      BigDecimal totalNumCnfAvailable,
                                      BigDecimal qtaCnfAvailable,
                                      BigDecimal totalQtaToBeTaken,
                                      BigDecimal totalNumCnfToBeTaken,
                                      BigDecimal qtaCnfToBeTaken,
                                      String partitaMag,
                                      Date dataScad,
                                      boolean canOverflowOrderQuantity,
                                      boolean canPartitaMagBeChanged,
                                      boolean canLUBeClosed,
                                      RunnableArgss<PickedQuantityDTO, Boolean> onComplete) {
        if (this.mListener != null) mListener.onItemDispatched(pickingObjectDTO,
                mtbAart,
                initialNumCnf,
                initialQtaCnf,
                initialQtaTot,
                totalQtaAvailable,
                totalNumCnfAvailable,
                qtaCnfAvailable,
                totalQtaToBeTaken,
                totalNumCnfToBeTaken,
                qtaCnfToBeTaken,
                partitaMag,
                dataScad,
                canOverflowOrderQuantity,
                canPartitaMagBeChanged,
                canLUBeClosed,
                onComplete);
    }

    private void sendOnRowSaved() {
        if (this.mListener != null) mListener.onRowSaved();
    }

    public PickingLiberoViewModel setListener(Listener listener) {
        this.mListener = listener;
        return this;
    }

    public interface Listener extends ILoadingListener, ILUBaseOperationsListener {

        void onError(Exception ex);

        void onLUClienteRequired(RunnableArgss<VtbDest, String> onComplete, Runnable onAbort);

        void onArtSelectionRequest(List<MtbColr> mtbColrsToPick, MtbAart mtbAart, RunnableArgs<List<MtbColr>> onComplete, Runnable onAbort);

        void onItemDispatched(PickingObjectDTO pickingObjectDTO,
                              MtbAart mtbAart,
                              BigDecimal initialNumCnf,
                              BigDecimal initialQtaCnf,
                              BigDecimal initialQtaTot,
                              BigDecimal totalQtaAvailable,
                              BigDecimal totalNumCnfAvailable,
                              BigDecimal qtaCnfAvailable,
                              BigDecimal totalQtaToBeTaken,
                              BigDecimal totalNumCnfToBeTaken,
                              BigDecimal qtaCnfToBeTaken,
                              String partitaMag,
                              Date dataScad,
                              boolean canOverflowOrderQuantity,
                              boolean canPartitaMagBeChanged,
                              boolean canLUBeClosed,
                              RunnableArgss<PickedQuantityDTO, Boolean> onComplete);
    }


    public MtbColt getCurrentMtbColt() {
        return mCurrentMtbColt;
    }

}
