package it.integry.integrywmsnative.gest.prod_recupero_materiale;

import androidx.databinding.ObservableArrayList;
import androidx.lifecycle.MutableLiveData;

import com.annimon.stream.Stream;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

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.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
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.rest.consumers.ColliMagazzinoRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.PrinterRESTConsumer;
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.UtilityString;
import it.integry.integrywmsnative.gest.prod_recupero_materiale.dto.HistoryVersamentoProdULDTO;
import it.integry.integrywmsnative.gest.prod_recupero_materiale.rest.ProdRecuperMaterialeRESTConsumer;
import it.integry.integrywmsnative.gest.spedizione.model.PickedQuantityDTO;

public class ProdRecuperoMaterialeViewModel {

    private final ProdRecuperMaterialeRESTConsumer mProdRecuperMaterialeRESTConsumer;
    private final ColliMagazzinoRESTConsumer mColliMagazzinoRESTConsumer;
    private final PrinterRESTConsumer mPrinterRESTConsumer;

    private final MutableLiveData<List<HistoryVersamentoProdULDTO>> mUlList = new MutableLiveData<>();

    private Listener mListener;


    @Inject
    public ProdRecuperoMaterialeViewModel(ProdRecuperMaterialeRESTConsumer prodRecuperMaterialeRESTConsumer, ColliMagazzinoRESTConsumer colliMagazzinoRESTConsumer, PrinterRESTConsumer printerRESTConsumer) {
        this.mProdRecuperMaterialeRESTConsumer = prodRecuperMaterialeRESTConsumer;
        this.mColliMagazzinoRESTConsumer = colliMagazzinoRESTConsumer;
        this.mPrinterRESTConsumer = printerRESTConsumer;
    }

    public void init(String codJfas) {
        this.sendOnLoadingStarted();
        new Thread(() -> {

            mProdRecuperMaterialeRESTConsumer.loadLastULVersate(codJfas, ulList -> {
                this.mUlList.setValue(ulList);

                this.sendOnLoadingEnded();
            }, this::sendError);

        }).start();
    }

    public void processBarcodeDTO(BarcodeScanDTO data) {
        if (UtilityBarcode.isEtichettaAnonima(data) || UtilityBarcode.isEtichetta128(data)) {
            this.executeEtichettaUL(data);
        }
    }

    private void executeEtichettaUL(BarcodeScanDTO barcodeScanDTO) {
        this.sendOnLoadingStarted();

        this.mColliMagazzinoRESTConsumer.getBySSCC(barcodeScanDTO.getStringValue(), true, false, mtbColt -> {
            this.sendOnLoadingEnded();

            if (mtbColt != null) {
                HistoryVersamentoProdULDTO historyVersamentoProdULRestDTO = this.getHistoryElementFromMtbColt(mtbColt);
                if (historyVersamentoProdULRestDTO != null) {
                    this.dispatchItem(historyVersamentoProdULRestDTO, mtbColt);
                } else {
                    this.sendError(new NoLUFoundException());
                }

            } else {
                this.sendError(new NoLUFoundException());
            }

        }, this::sendError);
    }


    private HistoryVersamentoProdULDTO getHistoryElementFromMtbColt(MtbColt mtbColt) {
        List<HistoryVersamentoProdULDTO> filteredItems = Stream.of(this.mUlList.getValue())
                .filter(x -> Objects.equals(x.getNumColloRif(), mtbColt.getNumCollo()) &&
                        x.getDataColloRif().equals(mtbColt.getDataColloS()) &&
                        x.getSerColloRif().equalsIgnoreCase(mtbColt.getSerCollo()) &&
                        x.getGestioneRif().equalsIgnoreCase(mtbColt.getGestione()))
                .toList();

        if (filteredItems != null && filteredItems.size() > 0) {
            return filteredItems.get(0);
        }

        return null;
    }


    public void dispatchItem(HistoryVersamentoProdULDTO item, MtbColt sourceMtbColt) {
        BigDecimal qtaDaEvadere = BigDecimal.ZERO;
        BigDecimal numCnfDaEvadere = BigDecimal.ZERO;

        qtaDaEvadere = qtaDaEvadere.add(item.getQtaCol());

        if (!UtilityBigDecimal.equalsTo(qtaDaEvadere, BigDecimal.ZERO)) {

            if(item.getMtbAart().isFlagQtaCnfFissaBoolean())
                numCnfDaEvadere = UtilityBigDecimal.divide(qtaDaEvadere, item.getQtaCnf());
            else numCnfDaEvadere = item.getNumCnf();
        }

        this.sendOnItemDispatched(item,
                sourceMtbColt,
                item.getMtbAart(),
                BigDecimal.ONE,
                item.getQtaCnf(),
                BigDecimal.ONE.multiply(item.getQtaCnf()),
                qtaDaEvadere,
                numCnfDaEvadere,
                item.getQtaCnf(),
                item.getPartitaMag(),
                false,
                false);
    }

    public void onItemDispatched(HistoryVersamentoProdULDTO item, PickedQuantityDTO pickedQuantityDTO, MtbColt sourceMtbColt) {

        this.sendOnLoadingStarted();

        BigDecimal inputNumCnf = pickedQuantityDTO.getNumCnf();
        BigDecimal inputQtaTot = pickedQuantityDTO.getQtaTot();
        BigDecimal inputQtaCnf = pickedQuantityDTO.getQtaCnf();

        final MtbColt mtbColtScarico = new MtbColt()
                .setDataCollo(item.getDataCollo())
                .setSerCollo(item.getSerCollo())
                .setNumCollo(item.getNumCollo())
                .setGestione(item.getGestione())
                .setMtbColr(new ObservableArrayList<>());


        BigDecimal totalSumOfQtaCol = BigDecimal.ZERO;
        BigDecimal totalSumOfNumCnf = BigDecimal.ZERO;

        if (SettingsManager.iDB().isFlagVersamentoDirettoProduzione()) {
            for (HistoryVersamentoProdULDTO.OrdineDto ordine : item.getOrdini()) {

                BigDecimal qtaColToSave;
                BigDecimal numCnfToSave;

                if (SettingsManager.iDB().isFlagForceAllToColli() || (item.getMtbAart() != null && !item.getMtbAart().isFlagQtaCnfFissaBoolean())) {
                    numCnfToSave = UtilityBigDecimal.divideAndRoundToInteger(inputNumCnf.multiply(BigDecimal.valueOf(ordine.getPercentageHr())), BigDecimal.valueOf(100), RoundingMode.FLOOR);
                    qtaColToSave = numCnfToSave.multiply(inputQtaCnf).setScale(0, BigDecimal.ROUND_FLOOR);
                } else {
                    qtaColToSave = UtilityBigDecimal.divideAndRoundToInteger(inputQtaTot.multiply(BigDecimal.valueOf(ordine.getPercentageHr())), BigDecimal.valueOf(100), RoundingMode.FLOOR);
                    numCnfToSave = UtilityBigDecimal.divide(qtaColToSave, item.getQtaCnf(), RoundingMode.FLOOR);
                }


                totalSumOfQtaCol = totalSumOfQtaCol.add(qtaColToSave);
                totalSumOfNumCnf = totalSumOfNumCnf.add(numCnfToSave);

                numCnfToSave = numCnfToSave.multiply(BigDecimal.valueOf(-1));
                qtaColToSave = qtaColToSave.multiply(BigDecimal.valueOf(-1));


                final MtbColr mtbColrScarico = new MtbColr()
                        .setCodMart(item.getCodMart())
                        .setPartitaMag(UtilityString.empty2null(item.getPartitaMag()))
                        .setQtaCol(qtaColToSave)
                        .setQtaCnf(inputQtaCnf)
                        .setNumCnf(numCnfToSave)
                        .setDescrizione(UtilityString.isNullOrEmpty(item.getMtbAart().getDescrizioneEstesa()) ? item.getMtbAart().getDescrizione() : item.getMtbAart().getDescrizioneEstesa())
                        .setDatetimeRow(UtilityDate.getDateInstance())
                        .setNumColloRif(item.getNumColloRif())
                        .setDataColloRif(item.getDataColloRif())
                        .setGestioneRif(item.getGestioneRif())
                        .setSerColloRif(item.getSerColloRif())
                        .setDataOrd(ordine.getData())
                        .setNumOrd(ordine.getNumero())
                        .setRigaOrd(ordine.getRigaOrd());

                mtbColrScarico.setOperation(CommonModelConsts.OPERATION.INSERT);
                mtbColtScarico.getMtbColr().add(mtbColrScarico);
            }
        } else {
            BigDecimal qtaColToSave;
            BigDecimal numCnfToSave;
            if (SettingsManager.iDB().isFlagForceAllToColli() || (item.getMtbAart() != null && !item.getMtbAart().isFlagQtaCnfFissaBoolean())) {
                numCnfToSave = inputNumCnf;
                qtaColToSave = numCnfToSave.multiply(inputQtaCnf).setScale(0, BigDecimal.ROUND_FLOOR);
            } else {
                qtaColToSave = inputQtaTot;
                numCnfToSave = UtilityBigDecimal.divide(qtaColToSave, item.getQtaCnf(), RoundingMode.FLOOR);
            }

            totalSumOfQtaCol = qtaColToSave;
            totalSumOfNumCnf = numCnfToSave;

            numCnfToSave = numCnfToSave.multiply(BigDecimal.valueOf(-1));
            qtaColToSave = qtaColToSave.multiply(BigDecimal.valueOf(-1));

            final MtbColr mtbColrScarico = new MtbColr()
                    .setCodMart(item.getCodMart())
                    .setPartitaMag(UtilityString.empty2null(item.getPartitaMag()))
                    .setQtaCol(qtaColToSave)
                    .setQtaCnf(inputQtaCnf)
                    .setNumCnf(numCnfToSave)
                    .setDescrizione(UtilityString.isNullOrEmpty(item.getMtbAart().getDescrizioneEstesa()) ? item.getMtbAart().getDescrizione() : item.getMtbAart().getDescrizioneEstesa())
                    .setDatetimeRow(UtilityDate.getDateInstance())
                    .setNumColloRif(item.getNumColloRif())
                    .setDataColloRif(item.getDataColloRif())
                    .setGestioneRif(item.getGestioneRif())
                    .setSerColloRif(item.getSerColloRif());

            mtbColrScarico.setOperation(CommonModelConsts.OPERATION.INSERT);
            mtbColtScarico.getMtbColr().add(mtbColrScarico);
        }

        boolean updateQtaCnfFirstRow = false;

        if (UtilityBigDecimal.lowerThan(totalSumOfNumCnf, inputNumCnf)) {
            BigDecimal diff = inputNumCnf.subtract(totalSumOfNumCnf).multiply(BigDecimal.valueOf(-1));
            mtbColtScarico.getMtbColr().get(0).setNumCnf(mtbColtScarico.getMtbColr().get(0).getNumCnf().add(diff));
            updateQtaCnfFirstRow = true;
        }

        if (UtilityBigDecimal.lowerThan(totalSumOfQtaCol, inputQtaTot)) {
            BigDecimal diff = inputQtaTot.subtract(totalSumOfQtaCol).multiply(BigDecimal.valueOf(-1));
            mtbColtScarico.getMtbColr().get(0).setQtaCol(mtbColtScarico.getMtbColr().get(0).getQtaCol().add(diff));
            updateQtaCnfFirstRow = true;
        }

        if (updateQtaCnfFirstRow) {
            mtbColtScarico.getMtbColr().get(0).setQtaCnf(
                    UtilityBigDecimal.divide(mtbColtScarico.getMtbColr().get(0).getQtaCol(), mtbColtScarico.getMtbColr().get(0).getNumCnf()));
        }

        for (int i = 0; i < mtbColtScarico.getMtbColr().size(); i++) {
            if (mtbColtScarico.getMtbColr().get(i).getQtaCol().equals(BigDecimal.ZERO)) {
                mtbColtScarico.getMtbColr().remove(i);
                i--;
            }
        }

        mtbColtScarico.setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);


        RunnableArgss<MtbColt, Boolean> saveRunnable = (mtbColt, createdLU) -> {

            if (mtbColt != null) {

                List<MtbColt> colliToSave = new ArrayList<>();
                boolean shouldPrint = false;

                if (!Objects.equals(mtbColt.getNumCollo(), item.getNumColloRif()) ||
                        !Objects.equals(mtbColt.getSerCollo(), item.getSerColloRif()) ||
                        !Objects.equals(mtbColt.getDataColloS(), item.getDataColloRif()) ||
                        !Objects.equals(mtbColt.getGestione(), item.getGestioneRif())) {

                    final MtbColt mtbColtCarico = mtbColt
                            .setMtbColr(new ObservableArrayList<>());
                    mtbColtCarico
                            .setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

                    final MtbColr mtbColrCarico = new MtbColr()
                            .setCodMart(item.getCodMart())
                            .setPartitaMag(UtilityString.empty2null(item.getPartitaMag()))
                            .setQtaCol(inputQtaTot)
                            .setNumCnf(inputNumCnf)
                            .setQtaCnf(inputQtaCnf)
                            .setDescrizione(UtilityString.isNullOrEmpty(item.getMtbAart().getDescrizioneEstesa()) ? item.getMtbAart().getDescrizione() : item.getMtbAart().getDescrizioneEstesa())
                            .setDatetimeRow(UtilityDate.getDateInstance())
                            .setNumColloRif(item.getNumColloRif())
                            .setDataColloRif(item.getDataColloRif())
                            .setGestioneRif(item.getGestioneRif())
                            .setSerColloRif(item.getSerColloRif());


                    mtbColtCarico.setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);
                    mtbColtCarico.getMtbColr().add(mtbColrCarico);

                    colliToSave.add(mtbColtCarico);
                    shouldPrint = true;
                }

                colliToSave.add(mtbColtScarico);

                boolean finalShouldPrint = shouldPrint;
                this.mColliMagazzinoRESTConsumer.saveColli(colliToSave, value -> {

//                    FBToast.successToast(mContext, mContext.getResources().getString(R.string.data_saved), FBToast.LENGTH_SHORT);
//
                    if (finalShouldPrint) {
                        printCollo(value.get(0), this::sendOnDataSaved);
                    } else {
                        this.sendOnDataSaved();
                    }

                }, this::sendError);

            } else {
                this.sendOnLoadingEnded();
            }
        };

        if (sourceMtbColt != null) saveRunnable.run(sourceMtbColt, false);
        else this.sendOnLURequest(true, false, saveRunnable);
    }


    private void printCollo(MtbColt mtbColtToPrint, Runnable onComplete) {
        singlePrint(mtbColtToPrint, onComplete, ex -> this.sendOnLUPrintError(ex, onComplete));
    }


    private void singlePrint(MtbColt mtbColtToPrint, Runnable onComplete, RunnableArgs<Exception> onAbort) {
        this.mPrinterRESTConsumer.printCollo(
                mtbColtToPrint,
                onComplete, onAbort);

    }


    public MutableLiveData<List<HistoryVersamentoProdULDTO>> getOrderList() {
        return mUlList;
    }

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

    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 sendOnItemDispatched(HistoryVersamentoProdULDTO item,
                                      MtbColt sourceMtbColt,
                                      MtbAart mtbAart,
                                      BigDecimal initialNumCnf,
                                      BigDecimal initialQtaCnf,
                                      BigDecimal initialQtaTot,
                                      BigDecimal totalQtaAvailable,
                                      BigDecimal totalNumCnfAvailable,
                                      BigDecimal qtaCnfAvailable,
                                      String partitaMag,
                                      boolean canOverflowOrderQuantity,
                                      boolean canLUBeClosed) {
        if (this.mListener != null)
            mListener.onItemDispatched(item, sourceMtbColt, mtbAart, initialNumCnf, initialQtaCnf, initialQtaTot, totalQtaAvailable, totalNumCnfAvailable, qtaCnfAvailable, partitaMag, canOverflowOrderQuantity, canLUBeClosed);
    }

    private void sendOnLURequest(boolean canLUBeCreated, boolean shouldCheckIfDocExist, RunnableArgss<MtbColt, Boolean> onComplete) {
        if (this.mListener != null)
            mListener.onLURequest(canLUBeCreated, shouldCheckIfDocExist, onComplete);
    }

    private void sendOnLUPrintError(Exception ex, Runnable onComplete) {
        if (this.mListener != null) mListener.onLUPrintError(ex, onComplete);
    }

    private void sendOnNoLUFound(Runnable onComplete) {
        if (this.mListener != null) mListener.onNoLUFound(onComplete);
    }

    private void sendOnDataSaved() {
        if (this.mListener != null) mListener.onDataSaved();
    }


    public interface Listener extends ILoadingListener {

        void onError(Exception ex);

        void onItemDispatched(HistoryVersamentoProdULDTO item,
                              MtbColt sourceMtbColt,
                              MtbAart mtbAart,
                              BigDecimal initialNumCnf,
                              BigDecimal initialQtaCnf,
                              BigDecimal initialQtaTot,
                              BigDecimal totalQtaAvailable,
                              BigDecimal totalNumCnfAvailable,
                              BigDecimal qtaCnfAvailable,
                              String partitaMag,
                              boolean canOverflowOrderQuantity,
                              boolean canLUBeClosed);

        void onLURequest(boolean canLUBeCreated, boolean shouldCheckIfDocExist, RunnableArgss<MtbColt, Boolean> onComplete);

        void onLUPrintError(Exception ex, Runnable onComplete);

        void onNoLUFound(Runnable onComplete);

        void onDataSaved();
    }

}
