package it.integry.integrywmsnative.view.dialogs.input_quantity_v2;

import androidx.databinding.ObservableField;

import java.math.BigDecimal;
import java.util.Date;

import javax.inject.Inject;

import it.integry.barcode_base_android_library.model.BarcodeScanDTO;
import it.integry.integrywmsnative.core.expansion.RunnableArgsWithReturn;
import it.integry.integrywmsnative.core.model.MtbAart;
import it.integry.integrywmsnative.core.rest.consumers.BarcodeRESTConsumer;
import it.integry.integrywmsnative.core.rest.model.Ean13PesoModel;
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.view.dialogs.exception.InvalidBatchLotException;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidExpireDateException;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidNotesException;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidQtaCnfQuantityException;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidQuantityException;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.exception.OverflowNumCnfAvailableQuantityException;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.exception.OverflowNumCnfOrderedQuantityException;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.exception.OverflowQtaTotAvailableQuantityException;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.exception.OverflowQtaTotOrderedQuantityException;

public class DialogInputQuantityV2ViewModel {

    private final int UPDATE_QTA_CNF = 1;
    private final int UPDATE_QTA_TOT = 2;

    public ObservableField<MtbAart> mtbAart = new ObservableField<>();

    public ObservableField<Boolean> blockedNumCnf = new ObservableField<>();
    public ObservableField<Boolean> blockedQtaCnf = new ObservableField<>();
    public ObservableField<Boolean> blockedQtaTot = new ObservableField<>();

    public BigDecimal totalQtaOrd;
    public BigDecimal totalNumCnfOrd;
    public BigDecimal qtaCnfOrd;

    public BigDecimal totalQtaToBeTaken;
    public BigDecimal totalNumCnfToBeTaken;
    public BigDecimal qtaCnfToBeTaken;

    public BigDecimal totalQtaInWarehouse;
    public BigDecimal totalNumCnfInWarehouse;
    public BigDecimal totalQtaCommitted;
    public BigDecimal totalNumCnfCommitted;

    public BigDecimal totalQtaAvailable;
    public BigDecimal totalNumCnfAvailable;
    public BigDecimal qtaCnfAvailable;

    public Boolean shouldAskDataScad;
    public Boolean shouldShowDataScad;
    public Boolean isNoteMandatory;

    private BigDecimal initialNumCnf;
    private BigDecimal initialQtaCnf;
    private BigDecimal initialQtaTot;

    private BigDecimal internalNumCnf;
    private BigDecimal internalQtaCnf;
    private BigDecimal internalQtaTot;

    private String internalPartitaMag;
    private String internalNote;
    private Date internalDataScad;

    private RunnableArgsWithReturn<DialogInputQuantityV2ViewModel, String> suggestPartitaMagRunnable;

    private boolean canOverflowOrderQuantity;
    private boolean canPartitaMagBeChanged;
    private int onNumCnfInputChanged;
    private int onQtaTotInputChanged;

    private Listener mListener;

    private final BarcodeRESTConsumer mBarcodeRESTConsumer;

    @Inject
    public DialogInputQuantityV2ViewModel(BarcodeRESTConsumer barcodeRESTConsumer) {
        this.mBarcodeRESTConsumer = barcodeRESTConsumer;
    }

    public void init(int onNumCnfInputChanged, int onQtaTotInputChanged) {
        this.onNumCnfInputChanged = onNumCnfInputChanged;
        this.onQtaTotInputChanged = onQtaTotInputChanged;

        if (this.initialNumCnf != null && this.initialQtaTot != null && this.initialQtaCnf != null) {
            this.internalNumCnf = this.initialNumCnf;
            this.internalQtaCnf = this.initialQtaCnf;
            this.internalQtaTot = this.initialQtaTot;

        } else if (totalQtaToBeTaken != null) {
            this.internalNumCnf = totalNumCnfToBeTaken;
            this.internalQtaCnf = qtaCnfToBeTaken;
            this.internalQtaTot = totalQtaToBeTaken;

        } else if (UtilityBigDecimal.lowerThan(totalQtaAvailable, totalQtaOrd)) {
            this.internalNumCnf = totalNumCnfAvailable;
            this.internalQtaCnf = qtaCnfAvailable;
            this.internalQtaTot = totalQtaAvailable;

        } else {
            this.internalNumCnf = totalNumCnfOrd;
            this.internalQtaCnf = qtaCnfOrd;
            this.internalQtaTot = totalQtaOrd;
        }

        if (this.shouldAskDataScad == null) {
            this.shouldAskDataScad = mtbAart.get().isFlagTracciabilitaBoolean() && mtbAart.get().getGgScadPartita() != null && mtbAart.get().getGgScadPartita() > 0;
        }
        this.shouldShowDataScad = this.shouldAskDataScad || this.internalDataScad != null;

        this.blockedNumCnf.set(false);
        this.blockedQtaCnf.set(false);
        this.blockedQtaTot.set(false);

        this.mListener.onDataChanged();
    }

    public void processBarcodeDTO(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        if (UtilityBarcode.isEanPeso(barcodeScanDTO)) {
            Ean13PesoModel ean13PesoModel;

            try {
                ean13PesoModel = Ean13PesoModel.fromBarcode(barcodeScanDTO.getStringValue());
            } catch (Exception ex) {
                this.mListener.onError(ex);
                onComplete.run();
                return;
            }

            if (this.mtbAart.get() != null &&
                    this.mtbAart.get().getBarCode() != null &&
                    (ean13PesoModel.getPrecode().contains(this.mtbAart.get().getBarCode()) || this.mtbAart.get().getBarCode().contains(ean13PesoModel.getPrecode()))) {
                this.setQtaTot(BigDecimal.valueOf(ean13PesoModel.getPeso()));
                this.setNumCnf(UtilityBigDecimal.divideAndRoundToInteger(this.getQtaTot(), this.mtbAart.get().getQtaCnf()));

                if (!this.mtbAart.get().isFlagQtaCnfFissaBoolean()) {
                    if (UtilityBigDecimal.equalsTo(this.getNumCnf(), BigDecimal.ZERO))
                        this.setNumCnf(BigDecimal.ONE);
                    this.setQtaCnf(UtilityBigDecimal.divide(this.getQtaTot(), this.getNumCnf()));
                }

                this.unlockNumCnf();
                this.unlockQtaCnf();
            }

            onComplete.run();
        } else if (UtilityBarcode.isEtichetta128(barcodeScanDTO)) {
            this.mBarcodeRESTConsumer.decodeEan128(barcodeScanDTO, ean128Model -> {

                if (ean128Model == null) {
                    onComplete.run();
                    return;
                }

                if (canPartitaMagBeChanged) {

                    if (!UtilityString.isNullOrEmpty(ean128Model.BatchLot)) {
                        this.setPartitaMag(ean128Model.BatchLot);
                    }

                    try {
                        if (!UtilityString.isNullOrEmpty(ean128Model.BestBefore)) {
                            this.setDataScad(UtilityDate.recognizeDate(ean128Model.BestBefore));
                        } else if (!UtilityString.isNullOrEmpty(ean128Model.Expiry)) {
                            this.setDataScad(UtilityDate.recognizeDate(ean128Model.Expiry));
                        }
                    } catch (Exception ex) {
                        this.mListener.onError(ex);
                    }
                }

                if (ean128Model.Count != null && ean128Model.Count > 0) {
                    this.setNumCnf(BigDecimal.valueOf(ean128Model.Count));
                } else if (ean128Model.NetWeightKg != null && ean128Model.NetWeightKg > 0) {
                    this.setQtaTot(BigDecimal.valueOf(ean128Model.NetWeightKg));
                    this.setNumCnf(UtilityBigDecimal.divideAndRoundToInteger(this.getQtaTot(), this.mtbAart.get().getQtaCnf()));

                    if (!this.mtbAart.get().isFlagQtaCnfFissaBoolean()) {
                        if (UtilityBigDecimal.equalsTo(this.getNumCnf(), BigDecimal.ZERO))
                            this.setNumCnf(BigDecimal.ONE);
                        this.setQtaCnf(UtilityBigDecimal.divide(this.getQtaTot(), this.getNumCnf()));
                    }

                    this.unlockNumCnf();
                    this.unlockQtaCnf();
                }

                this.mListener.onDataChanged();
                onComplete.run();

            }, ex -> {
                this.mListener.onError(ex);
                onComplete.run();
            });
        } else {
            onComplete.run();
        }
    }

    public String getPartitaMag() {
        return internalPartitaMag;
    }

    public String getNote() {
        return internalNote;
    }

    public Date getDataScad() {
        return internalDataScad;
    }

    public BigDecimal getNumCnf() {
        return this.internalNumCnf;
    }

    public BigDecimal getQtaCnf() {
        return this.internalQtaCnf;
    }

    public BigDecimal getQtaTot() {
        return this.internalQtaTot;
    }

    public DialogInputQuantityV2ViewModel setMtbAart(MtbAart mtbAart) {
        this.mtbAart.set(mtbAart);
        return this;
    }

    public DialogInputQuantityV2ViewModel setInitialNumCnf(BigDecimal initialNumCnf) {
        this.initialNumCnf = initialNumCnf;
        return this;
    }

    public DialogInputQuantityV2ViewModel setInitialQtaCnf(BigDecimal initialQtaCnf) {
        this.initialQtaCnf = initialQtaCnf;
        return this;
    }

    public DialogInputQuantityV2ViewModel setInitialQtaTot(BigDecimal initialQtaTot) {
        this.initialQtaTot = initialQtaTot;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalQtaInWarehouse(BigDecimal totalQtaInWarehouse) {
        this.totalQtaInWarehouse = totalQtaInWarehouse;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalNumCnfInWarehouse(BigDecimal totalNumCnfInWarehouse) {
        this.totalNumCnfInWarehouse = totalNumCnfInWarehouse;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalQtaCommitted(BigDecimal totalQtaCommitted) {
        this.totalQtaCommitted = totalQtaCommitted;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalNumCnfCommitted(BigDecimal totalNumCnfCommitted) {
        this.totalNumCnfCommitted = totalNumCnfCommitted;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalQtaOrd(BigDecimal totalQtaOrd) {
        this.totalQtaOrd = totalQtaOrd;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalNumCnfOrd(BigDecimal totalNumCnfOrd) {
        this.totalNumCnfOrd = totalNumCnfOrd;
        return this;
    }

    public DialogInputQuantityV2ViewModel setQtaCnfOrd(BigDecimal qtaCnfOrd) {
        this.qtaCnfOrd = qtaCnfOrd;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalQtaToBeTaken(BigDecimal totalQtaToBeTaken) {
        this.totalQtaToBeTaken = totalQtaToBeTaken;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalNumCnfToBeTaken(BigDecimal totalNumCnfToBeTaken) {
        this.totalNumCnfToBeTaken = totalNumCnfToBeTaken;
        return this;
    }

    public DialogInputQuantityV2ViewModel setQtaCnfToBeTaken(BigDecimal qtaCnfToBeTaken) {
        this.qtaCnfToBeTaken = qtaCnfToBeTaken;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalQtaAvailable(BigDecimal totalQtaAvailable) {
        this.totalQtaAvailable = totalQtaAvailable;
        return this;
    }

    public DialogInputQuantityV2ViewModel setTotalNumCnfAvailable(BigDecimal totalNumCnfAvailable) {
        this.totalNumCnfAvailable = totalNumCnfAvailable;
        return this;
    }

    public DialogInputQuantityV2ViewModel setQtaCnfAvailable(BigDecimal qtaCnfAvailable) {
        this.qtaCnfAvailable = qtaCnfAvailable;
        return this;
    }

    public DialogInputQuantityV2ViewModel setCanOverflowOrderQuantity(boolean canOverflowOrderQuantity) {
        this.canOverflowOrderQuantity = canOverflowOrderQuantity;
        return this;
    }

    public DialogInputQuantityV2ViewModel setCanPartitaMagBeChanged(boolean canPartitaMagBeChanged) {
        this.canPartitaMagBeChanged = canPartitaMagBeChanged;
        return this;
    }

    public boolean canPartitaMagBeChanged() {
        return canPartitaMagBeChanged;
    }

    public DialogInputQuantityV2ViewModel setPartitaMag(String internalPartitaMag) {
        this.internalPartitaMag = internalPartitaMag;
        return this;
    }

    public DialogInputQuantityV2ViewModel setDataScad(Date internalDataScad) {
        this.internalDataScad = internalDataScad;
        return this;
    }

    public void setNumCnf(BigDecimal newValue) {
        this.internalNumCnf = newValue;
        this.blockedNumCnf.set(!(UtilityBigDecimal.equalsTo(newValue, BigDecimal.ZERO) || newValue == null));
        if (newValue == null) return;
//        if (UtilityBigDecimal.equalsTo(newValue, BigDecimal.ZERO))

        if (UtilityBigDecimal.greaterThan(newValue, totalNumCnfAvailable))
            newValue = totalNumCnfAvailable;

        //Se è piu' grande della qta da prelevare e non posso aggiungere qta extra allora ricalcolo
        if (!canOverflowOrderQuantity) {
            newValue = UtilityBigDecimal.getLowerBetween(newValue, totalNumCnfOrd);
            this.internalNumCnf = newValue;
            this.mListener.onDataChanged();
//            return;
        }

        switch (onNumCnfInputChanged) {
            case UPDATE_QTA_CNF:
                if (!this.blockedQtaCnf.get() && !this.mtbAart.get().isFlagQtaCnfFissaBoolean() && this.internalQtaTot != null)
                    this.internalQtaCnf = UtilityBigDecimal.divide(internalQtaTot, newValue);
                else if (!this.blockedQtaTot.get() && this.internalQtaCnf != null)
                    this.internalQtaTot = UtilityBigDecimal.multiply(newValue, this.internalQtaCnf);
                break;

            case UPDATE_QTA_TOT:
                if (!this.blockedQtaTot.get() && this.internalQtaCnf != null)
                    this.internalQtaTot = UtilityBigDecimal.multiply(newValue, this.internalQtaCnf);
                else if (!this.blockedQtaCnf.get() && !this.mtbAart.get().isFlagQtaCnfFissaBoolean() && this.internalQtaTot != null)
                    this.internalQtaCnf = UtilityBigDecimal.divide(internalQtaTot, newValue);
                break;

        }

        this.mListener.onDataChanged();
    }

    public void setQtaCnf(BigDecimal newValue) {
        this.internalQtaCnf = newValue;
        this.blockedQtaCnf.set(!(UtilityBigDecimal.equalsTo(newValue, BigDecimal.ZERO) || newValue == null));
        if (newValue == null) return;
//        if (UtilityBigDecimal.equalsTo(newValue, BigDecimal.ZERO))

        if (!this.blockedQtaTot.get() && this.internalNumCnf != null)
            this.internalQtaTot = UtilityBigDecimal.multiply(newValue, this.internalNumCnf);
        else if (!this.blockedNumCnf.get() && this.internalQtaTot != null)
            this.internalNumCnf = UtilityBigDecimal.divide(internalQtaTot, newValue);

        this.mListener.onDataChanged();
    }

    public void setQtaTot(BigDecimal newValue) {
        this.internalQtaTot = newValue;
        this.blockedQtaTot.set(!(UtilityBigDecimal.equalsTo(newValue, BigDecimal.ZERO) || newValue == null));
        if (newValue == null) return;
//        if (UtilityBigDecimal.equalsTo(newValue, BigDecimal.ZERO))

        newValue = UtilityBigDecimal.getLowerBetween(newValue, totalQtaAvailable);

        //Se è piu' grande della qta da prelevare e non posso aggiungere qta extra allora ricalcolo
        if (!canOverflowOrderQuantity && this.mtbAart.get().isFlagQtaCnfFissaBoolean()) {
            newValue = UtilityBigDecimal.getLowerBetween(newValue, totalQtaOrd);
            this.internalQtaTot = newValue;
            this.mListener.onDataChanged();
//            return;
        }


        switch (onNumCnfInputChanged) {
            case UPDATE_QTA_CNF:
                if (!this.blockedQtaCnf.get() && !this.mtbAart.get().isFlagQtaCnfFissaBoolean() && this.internalNumCnf != null)
                    this.internalQtaCnf = UtilityBigDecimal.divide(newValue, internalNumCnf);
                else if (!this.blockedNumCnf.get() && this.internalQtaCnf != null)
                    this.internalNumCnf = UtilityBigDecimal.divide(newValue, internalQtaCnf);
                break;

            case UPDATE_QTA_TOT:
                if (!this.blockedNumCnf.get() && this.internalQtaCnf != null)
                    this.internalNumCnf = UtilityBigDecimal.divide(newValue, internalQtaCnf);
                else if (!this.blockedQtaCnf.get() && !this.mtbAart.get().isFlagQtaCnfFissaBoolean() && this.internalNumCnf != null)
                    this.internalQtaCnf = UtilityBigDecimal.divide(newValue, internalNumCnf);
                break;

        }

        this.mListener.onDataChanged();
    }

    private void unlockNumCnf() {
        this.blockedNumCnf.set(false);
    }

    private void unlockQtaCnf() {
        this.blockedQtaCnf.set(false);
    }

    private void unlockQtaCol() {
        this.blockedQtaTot.set(false);
    }

    public boolean validate() {
        if (this.internalQtaCnf == null || UtilityBigDecimal.equalsTo(this.internalQtaCnf, BigDecimal.ZERO)) {

            this.mListener.onError(new InvalidQtaCnfQuantityException());
            return false;

        }
        if (mtbAart.get().isFlagTracciabilitaBoolean() && UtilityString.isNullOrEmpty(internalPartitaMag)) {
            if (suggestPartitaMagRunnable != null) {
                internalPartitaMag = suggestPartitaMagRunnable.run(this);
                if (internalPartitaMag == null) {
                    this.mListener.onError(new InvalidBatchLotException());
                    return false;
                }
            } else {
                this.mListener.onError(new InvalidBatchLotException());
                return false;
            }

        }
        if (this.shouldAskDataScad && UtilityString.isNullOrEmpty(internalPartitaMag)) {

            this.mListener.onError(new InvalidBatchLotException());
            return false;

        }
        if (this.shouldAskDataScad && this.internalDataScad == null) {

            this.mListener.onError(new InvalidExpireDateException());
            return false;

        }
        if (!this.canOverflowOrderQuantity && UtilityBigDecimal.greaterThan(this.internalQtaTot, this.totalQtaOrd) &&
                this.mtbAart.get().isFlagQtaCnfFissaBoolean()) {

            this.mListener.onError(new OverflowQtaTotOrderedQuantityException());
            return false;

        }
        if (!this.canOverflowOrderQuantity && UtilityBigDecimal.greaterThan(this.internalNumCnf, this.totalNumCnfOrd)) {

            this.mListener.onError(new OverflowNumCnfOrderedQuantityException());
            return false;

        }
        if (UtilityBigDecimal.greaterThan(this.internalQtaTot, this.totalQtaAvailable)) {

            this.mListener.onError(new OverflowQtaTotAvailableQuantityException());
            return false;

        }
        if (UtilityBigDecimal.greaterThan(this.internalNumCnf, this.totalNumCnfAvailable)) {

            this.mListener.onError(new OverflowNumCnfAvailableQuantityException());
            return false;

        }
        if (this.internalNumCnf == null || this.internalQtaCnf == null || this.internalQtaTot == null ||
                UtilityBigDecimal.equalsOrLowerThan(this.internalNumCnf, BigDecimal.ZERO) ||
                UtilityBigDecimal.equalsOrLowerThan(this.internalQtaCnf, BigDecimal.ZERO) ||
                UtilityBigDecimal.equalsOrLowerThan(this.internalQtaTot, BigDecimal.ZERO)) {

            this.mListener.onError(new InvalidQuantityException());
            return false;

        }
        if (this.isNoteMandatory() && (this.internalNote == null || this.internalNote.length() <= 0)) {
            this.mListener.onError(new InvalidNotesException());
            return false;
        }

        if (!UtilityString.isNullOrEmpty(this.internalPartitaMag))
            this.internalPartitaMag = this.internalPartitaMag.toUpperCase();
        return true;
    }

    public DialogInputQuantityV2ResultDTO getResult() {
        return new DialogInputQuantityV2ResultDTO()
                .setPartitaMag(this.internalPartitaMag)
                .setDataScad(this.internalDataScad)
                .setNumCnf(this.internalNumCnf)
                .setQtaCnf(this.internalQtaCnf)
                .setNote(this.internalNote)
                .setQtaTot(this.internalQtaTot);

    }

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

    public DialogInputQuantityV2ViewModel setNote(String value) {
        this.internalNote = value;
        return this;
    }

    public DialogInputQuantityV2ViewModel setSuggestPartitaMagRunnable(RunnableArgsWithReturn<DialogInputQuantityV2ViewModel, String> suggestPartitaMagRunnable) {
        this.suggestPartitaMagRunnable = suggestPartitaMagRunnable;
        return this;
    }

    public Boolean isNoteMandatory() {
        return isNoteMandatory;
    }

    public DialogInputQuantityV2ViewModel setIsNoteMandatory(Boolean noteMandatory) {
        isNoteMandatory = noteMandatory;
        return this;
    }

    public MtbAart getMtbAart() {
        return this.mtbAart.get();
    }

    public interface Listener {
        void onDataChanged();

        void onError(Exception ex);
    }
}
