package it.integry.integrywmsnative.gest.pv_ordine_acquisto_edit;

import androidx.lifecycle.MutableLiveData;

import com.annimon.stream.Stream;

import java.math.BigDecimal;
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.R;
import it.integry.integrywmsnative.core.data_store.db.entity.ArticoloGriglia;
import it.integry.integrywmsnative.core.data_store.db.entity.ArticoloOrdine;
import it.integry.integrywmsnative.core.data_store.db.entity.Griglia;
import it.integry.integrywmsnative.core.data_store.db.entity.Ordine;
import it.integry.integrywmsnative.core.data_store.db.repository.ArticoliOrdineRepository;
import it.integry.integrywmsnative.core.data_store.db.repository.ArticoloGrigliaRepository;
import it.integry.integrywmsnative.core.data_store.db.repository.GrigliaRepository;
import it.integry.integrywmsnative.core.data_store.db.repository.OrdineRepository;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.interfaces.viewmodel_listeners.ILoadingListener;
import it.integry.integrywmsnative.core.rest.consumers.PVOrdiniAcquistoRESTConsumer;
import it.integry.integrywmsnative.core.settings.SettingsManager;

public class PVOrdineAcquistoEditViewModel {

    @Inject
    OrdineRepository mOrdineRepository;

    @Inject
    GrigliaRepository mGrigliaRepository;

    @Inject
    ArticoliOrdineRepository mArticoliOrdineRepository;

    @Inject
    ArticoloGrigliaRepository mArticoloGrigliaRepository;

    @Inject
    PVOrdiniAcquistoRESTConsumer pvOrdiniAcquistoRESTConsumer;

    private Ordine mCurrentOrdine;


    private Listener mListener;


    private final MutableLiveData<Griglia> mCurrentGriglia = new MutableLiveData<>();
    private final MutableLiveData<List<ArticoloOrdine>> mArticoli = new MutableLiveData<>();


    public PVOrdineAcquistoEditViewModel(OrdineRepository ordineRepository,
                                         GrigliaRepository grigliaRepository,
                                         ArticoliOrdineRepository articoliOrdineRepository,
                                         ArticoloGrigliaRepository articoloGrigliaRepository,
                                         PVOrdiniAcquistoRESTConsumer pvOrdiniAcquistoRESTConsumer) {
        this.mOrdineRepository = ordineRepository;
        this.mGrigliaRepository = grigliaRepository;
        this.mArticoliOrdineRepository = articoliOrdineRepository;
        this.mArticoloGrigliaRepository = articoloGrigliaRepository;
        this.pvOrdiniAcquistoRESTConsumer = pvOrdiniAcquistoRESTConsumer;
    }

    public void init(Ordine ordine) {
        this.mCurrentOrdine = ordine;

        this.sendOnLoadingStarted();

        this.loadGriglia(ordine.getIdGriglia(), griglia -> {
            mCurrentGriglia.postValue(griglia);
            this.loadArticoli(mCurrentOrdine, articoli -> {
                articoli = Stream.of(articoli).map(this::setBgTint).toList();
                this.mArticoli.postValue(articoli);
                this.sendOnLoadingEnded();
            });
        });
    }

    private ArticoloOrdine setBgTint(ArticoloOrdine art) {
        int chk = art.checkQtaProposta();
        switch (chk) {
            case -1:
                art.setBgTint(this.mListener.getColorFromResource(R.color.red_600));
                break;
            case 1:
                art.setBgTint(this.mListener.getColorFromResource(R.color.orange_600));
                break;
            default:
                art.setBgTint(this.mListener.getColorFromResource(R.color.colorPrimary));
        }
        return art;
    }

    public MutableLiveData<Griglia> getCurrentGriglia() {
        return mCurrentGriglia;
    }

    public MutableLiveData<List<ArticoloOrdine>> getArticoli() {
        return mArticoli;
    }

    public void saveArticolo(ArticoloOrdine articolo) {
        setBgTint(articolo);
        var newList = this.mArticoli.getValue();

        var alreadySavedItem = Stream.of(newList)
                .filter(x -> Objects.equals(x.getCodMart(), articolo.getCodMart()))
                .findFirst();

        if (alreadySavedItem.isPresent()) newList.remove(alreadySavedItem.get());

        newList.add(articolo);
        this.mArticoli.postValue(newList);
    }


    public void processBarcodeDTO(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        this.checkArticolo(
                mCurrentOrdine,
                barcodeScanDTO.getStringValue(),
                articoloOrdine -> {
                    this.sendOnItemDispatched(articoloOrdine);
                    onComplete.run();
                },
                this::sendError
        );
    }


    private void loadGriglia(int idGriglia, RunnableArgs<Griglia> onLoad) {
        try {
            mGrigliaRepository.findGrigliaById(idGriglia, onLoad, this::sendError);
        } catch (Exception e) {
            this.sendError(e);
        }
    }

    private void loadArticoli(Ordine ordine, RunnableArgs<List<ArticoloOrdine>> onLoad) {
        try {
            mArticoliOrdineRepository.findArticoliByOrdine(ordine, onLoad, this::sendError);
        } catch (Exception e) {
            this.sendError(e);
        }
    }

    public void checkArticolo(Ordine ordine, String barcode, RunnableArgs<ArticoloOrdine> onSuccess, RunnableArgs<Exception> onFail) {
        try {
            mArticoloGrigliaRepository.findArticoloByScanAndGriglia(barcode, ordine.getIdGriglia(), artGrid -> {
                if (artGrid != null) {
                    mArticoliOrdineRepository.findArticoloByScanAndOrdine(ordine, barcode, (articolo) -> {
                        if (articolo != null) {
                            articolo
                                    .setQtaPrevistaVendita(BigDecimal.valueOf(artGrid.getQtaPrevistaVendita()))
                                    .setQtaProposta(BigDecimal.valueOf(artGrid.getQtaProposta()))
                                    .setGiacenza(BigDecimal.valueOf(artGrid.getGiacenza()))
                                    .setMediaSett(artGrid.getMediaSett());

                            articolo.setQtaMinOrdinabile(artGrid.getQtaMinOrdinabile());
                            articolo.setMerceDaRic(artGrid.getMerceDaRic());
                            articolo.setNewNoPromo(artGrid.isNewNoPromo());
                            articolo.setFlagQtaMultipla(artGrid.getFlagQtaMultipla());
                            articolo.setSystemNote(artGrid.generateSystemNote());

                            onSuccess.run(articolo);
                        } else {
                            ArticoloOrdine art = artGrid.convertToArticoloOrdine(ordine);
                            if (isAutoOrderOnScan()) {
                                art.setQtaOrd(art.getQtaCnf());
                                mArticoliOrdineRepository.saveArticoloToOrdine(art, () -> {
                                    saveArticolo(art);
                                    this.sendOnLoadingEnded();

                                }, this::sendError);
                            } else {
                                onSuccess.run(art);
                            }
                        }
                    }, onFail);

                } else {
                    onFail.run(new Exception("L' articolo ( " + barcode + " ) non  è presente nella griglia"));
                }
            }, onFail);


        } catch (Exception e) {
            onFail.run(e);
        }
    }

    private boolean isAutoOrderOnScan() {
        return SettingsManager.iDB().isFlagOrdinaArticoliOnScan();
    }

    private void getNewArticoli(int idGrigla, RunnableArgs<List<ArticoloGriglia>> onSuccess, RunnableArgs<Exception> onError) {
        mArticoloGrigliaRepository.findNewArticoliInGrigla(idGrigla, onSuccess, onError);
    }

    public void orderNewProducts() {
        this.sendOnLoadingStarted();

        this.getNewArticoli(mCurrentOrdine.getIdGriglia(), articoli -> {
            if (articoli != null && !articoli.isEmpty()) {
                List<ArticoloOrdine> articoliToSave = new ArrayList<>();

                Stream.of(articoli).forEach(articolo -> {
                    if (
                            Stream.of(mArticoli.getValue())
                                    .filter(x -> x.getCodMart().equalsIgnoreCase(articolo.getCodMart()))
                                    .findFirst()
                                    .isEmpty()

                                    && Stream.of(articoliToSave)
                                    .filter(x -> x.getCodMart().equalsIgnoreCase(articolo.getCodMart()))
                                    .findFirst()
                                    .isEmpty()
                    ) {
                        ArticoloOrdine dto = articolo.convertToArticoloOrdine(mCurrentOrdine);
                        articoliToSave.add(dto);
                    }
                });
                if (!articoliToSave.isEmpty()) {
                    mArticoliOrdineRepository.saveArticoliToOrdine(articoliToSave, () -> {
                        Stream.of(articoliToSave).forEach(this::saveArticolo);

                        this.sendOnLoadingEnded();

                    }, this::sendError);

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

    public void deleteArticolo(ArticoloOrdine articoloOrdine) {
        mArticoliOrdineRepository.deleteArticolo(articoloOrdine, () -> {
            this.mArticoli.getValue().remove(articoloOrdine);
            this.mArticoli.postValue(this.mArticoli.getValue());
        }, this::sendError);
    }

    public void exportOrdine(Runnable onComplete) {
        this.sendOnLoadingStarted();
        pvOrdiniAcquistoRESTConsumer.saveOrdine(mCurrentOrdine, mArticoli.getValue(), mCurrentGriglia.getValue(), (ordine) -> {

            mOrdineRepository.updateOrder(ordine, ord -> {
                this.sendOnLoadingEnded();
                onComplete.run();
            }, this::sendError);

        }, this::sendError);
    }

    private void sendOnItemDispatched(ArticoloOrdine articoloOrdine) {
        if (this.mListener != null) mListener.onItemDispatched(articoloOrdine);
    }

    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 interface Listener extends ILoadingListener {
        void onItemDispatched(ArticoloOrdine articoloOrdine);

        int getColorFromResource(int resId);

        void onError(Exception ex);
    }

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