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

import androidx.databinding.ObservableField;
import androidx.lifecycle.MutableLiveData;

import java.math.BigDecimal;
import java.util.List;

import javax.inject.Inject;

import it.integry.integrywmsnative.core.interfaces.viewmodel_listeners.ILoadingListener;
import it.integry.integrywmsnative.core.model.JtbFasi;
import it.integry.integrywmsnative.core.model.MtbAart;
import it.integry.integrywmsnative.core.model.MtbTCol;
import it.integry.integrywmsnative.core.rest.consumers.MagazzinoRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.MesRESTConsumer;
import it.integry.integrywmsnative.core.utility.UtilityBigDecimal;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidGrossWeightQuantityException;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidProductionLineException;
import it.integry.integrywmsnative.view.dialogs.exception.InvalidNumCnfQuantityException;

public class DialogInputLUProdViewModel {

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

    public ObservableField<Boolean> blockedNumCnf = new ObservableField<>(false);
    public ObservableField<Boolean> blockedPesoCollo = new ObservableField<>(false);
    public ObservableField<Boolean> blockedPesoNetto = new ObservableField<>(false);
    public ObservableField<Boolean> blockedTaraPed = new ObservableField<>(false);
    public ObservableField<Boolean> blockedTaraCol = new ObservableField<>(false);
    public ObservableField<Boolean> blockedPesoLordo = new ObservableField<>(false);

    private final MutableLiveData<List<MtbTCol>> mTipiColloLiveData = new MutableLiveData<>();
    private final MutableLiveData<List<JtbFasi>> mLineeProdLiveData = new MutableLiveData<>();

    private BigDecimal initialNumCnf;
    private BigDecimal initialPesoCollo;

    private BigDecimal internalNumCnf;
    private BigDecimal internalPesoCollo;
    private BigDecimal internalPesoNetto;
    private BigDecimal internalTaraPed;
    private BigDecimal internalTaraCol;
    private BigDecimal internalPesoLordo;

    private MtbTCol internalTipoPed;
    private JtbFasi internalLineaProd;

    private boolean isLineeProdRequired;

    private Listener mListener;

    private final MagazzinoRESTConsumer mMagazzinoRESTConsumer;
    private final MesRESTConsumer mMesRESTConsumer;

    @Inject
    public DialogInputLUProdViewModel(MagazzinoRESTConsumer magazzinoRESTConsumer, MesRESTConsumer mesRESTConsumer) {
        this.mMagazzinoRESTConsumer = magazzinoRESTConsumer;
        this.mMesRESTConsumer = mesRESTConsumer;
    }

    public void init(String codJfasParent) {
        new Thread(this::sendOnLoadingStarted).start();

        mMagazzinoRESTConsumer.getTipiCollo(tipiCollo -> {
            this.mTipiColloLiveData.postValue(tipiCollo);

            mMesRESTConsumer.getLineeProduzione(codJfasParent, jtbFasi -> {
                this.mLineeProdLiveData.postValue(jtbFasi);

                this.internalPesoLordo = calcPesoLordo();
                this.mListener.onDataChanged();

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

    public MutableLiveData<List<MtbTCol>> getTipiCollo() {
        return this.mTipiColloLiveData;
    }

    public MutableLiveData<List<JtbFasi>> getLineeProd() {
        return this.mLineeProdLiveData;
    }

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

    public BigDecimal getPesoCollo() {
        return this.internalPesoCollo;
    }

    public BigDecimal getPesoNetto() {
        return this.internalPesoNetto;
    }

    public BigDecimal getTaraPed() {
        return this.internalTaraPed;
    }

    public BigDecimal getTaraCol() {
        return this.internalTaraCol;
    }

    public BigDecimal getPesoLordo() {
        return this.internalPesoLordo;
    }

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

    public DialogInputLUProdViewModel setInitialNumCnf(BigDecimal numCnf) {
        this.initialNumCnf = numCnf;
        this.internalNumCnf = numCnf;
        return this;
    }

    public DialogInputLUProdViewModel setInitialPesoCollo(BigDecimal pesoCollo) {
        this.initialPesoCollo = pesoCollo;
        this.internalPesoCollo = pesoCollo;
        return this;
    }

    public DialogInputLUProdViewModel setInitialPesoNetto(BigDecimal pesoNetto) {
        this.internalPesoNetto = pesoNetto;
        return this;
    }


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

        if (this.internalNumCnf != null) {
            if (!this.blockedPesoLordo.get()) {
                this.internalPesoNetto = UtilityBigDecimal.multiply(numCnf, this.internalPesoCollo);
                this.internalPesoLordo = calcPesoLordo();
            } else {
                BigDecimal taraPed = internalTaraPed != null ? internalTaraPed : BigDecimal.ZERO;

                this.internalPesoCollo = UtilityBigDecimal.divide(this.internalPesoLordo.subtract(taraPed), internalNumCnf).subtract(internalTaraCol);
                this.internalPesoNetto = UtilityBigDecimal.multiply(this.internalNumCnf, internalPesoCollo);
            }

        }

        this.mListener.onDataChanged();
    }

    public void setPesoCollo(BigDecimal pesoCollo) {
        this.internalPesoCollo = pesoCollo;
        this.internalPesoLordo = calcPesoLordo();

        this.mListener.onDataChanged();
    }

    public void setPesoNetto(BigDecimal pesoNetto) {
        this.internalPesoNetto = pesoNetto;
    }

    public void setTaraPed(BigDecimal taraPed) {
        this.internalTaraPed = taraPed;

        if (!this.blockedPesoLordo.get()) {
            if(internalNumCnf == null) internalNumCnf = initialNumCnf;

            this.internalPesoNetto = UtilityBigDecimal.multiply(internalNumCnf, this.internalPesoCollo);
            this.internalPesoLordo = calcPesoLordo();
        } else {
            this.internalPesoCollo = UtilityBigDecimal.divide(this.internalPesoLordo.subtract(taraPed), internalNumCnf).subtract(internalTaraCol);
            this.internalPesoNetto = UtilityBigDecimal.multiply(this.internalNumCnf, internalPesoCollo);
        }

        this.mListener.onDataChanged();
    }

    public void setTaraCol(BigDecimal taraCol) {
        this.internalTaraCol = taraCol;
    }

    public void setPesoLordo(BigDecimal pesoLordo) {
        this.internalPesoLordo = pesoLordo;
        this.blockedPesoLordo.set(!(UtilityBigDecimal.equalsTo(pesoLordo, BigDecimal.ZERO) || pesoLordo == null));

        if (internalPesoLordo != null) {
            if(internalNumCnf == null) internalNumCnf = initialNumCnf;

            BigDecimal taraPed = internalTaraPed != null ? internalTaraPed : BigDecimal.ZERO;
            this.internalPesoCollo = UtilityBigDecimal.divide(this.internalPesoLordo.subtract(taraPed), internalNumCnf).subtract(internalTaraCol);
            this.internalPesoNetto = UtilityBigDecimal.multiply(this.internalNumCnf, internalPesoCollo);
        } else {
            this.internalPesoCollo = this.initialPesoCollo;
            this.internalPesoNetto = UtilityBigDecimal.multiply(this.internalNumCnf, internalPesoCollo);
        }

        this.mListener.onDataChanged();
    }

    public DialogInputLUProdViewModel setInternalTipoPed(MtbTCol internalTipoPed) {
        this.internalTipoPed = internalTipoPed;
        return this;
    }

    public DialogInputLUProdViewModel setInternalLineaProd(JtbFasi internalLineaProd) {
        this.internalLineaProd = internalLineaProd;
        return this;
    }

    public void refreshPesoLordo() {
        this.internalPesoLordo = this.calcPesoLordo();
    }

    private BigDecimal calcPesoLordo() {
        BigDecimal taraPed = internalTaraPed != null ? internalTaraPed : BigDecimal.ZERO;
        return UtilityBigDecimal.multiply(this.internalNumCnf, this.internalPesoCollo.add(internalTaraCol)).add(taraPed);
    }

    public DialogInputLUProdViewModel setLineeProdRequired(boolean lineeProdRequired) {
        isLineeProdRequired = lineeProdRequired;
        return this;
    }

    public boolean validate() {
        if (this.internalNumCnf == null || UtilityBigDecimal.equalsOrLowerThan(this.internalNumCnf, BigDecimal.ZERO)) {
            this.mListener.onError(new InvalidNumCnfQuantityException());
            return false;
        } else if (this.internalPesoLordo == null || UtilityBigDecimal.equalsOrLowerThan(this.internalPesoLordo, BigDecimal.ZERO)) {
            this.mListener.onError(new InvalidGrossWeightQuantityException());
            return false;
        } else if(this.internalLineaProd == null && isLineeProdRequired) {
            this.mListener.onError(new InvalidProductionLineException());
            return false;
        }

        return true;
    }


    public DialogInputLUProdResultDTO getResult() {
        return new DialogInputLUProdResultDTO()
                .setNumCnf(this.internalNumCnf)
                .setPesoCollo(this.internalPesoCollo)
                .setPesoNetto(this.internalPesoNetto)
                .setTaraPed(this.internalTaraPed)
                .setTaraCol(this.internalTaraCol)
                .setPesoLordo(this.internalPesoLordo)
                .setMtbTCol(this.internalTipoPed)
                .setJtbFasi(this.internalLineaProd);
    }


    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);
    }

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

    public interface Listener extends ILoadingListener {
        void onDataChanged();

        void onError(Exception ex);
    }

}
