package it.integry.integrywmsnative.gest.pv_ordine_acquisto_edit;

import androidx.lifecycle.MutableLiveData;

import com.annimon.stream.Stream;

import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;

import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.barcode_reader.BarcodeManager;
import it.integry.integrywmsnative.core.data_store.db.entity.ArticoloOrdine;
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.data_store.db.view_model.ArticoloDTO;
import it.integry.integrywmsnative.core.exception.NoArtsInGridException;
import it.integry.integrywmsnative.core.exception.NoResultFromBarcodeException;
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 List<ArticoloDTO> mArticoliGriglia = new ArrayList<>();
    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, List<ArticoloDTO> articoliGriglia) {
        this.mCurrentOrdine = ordine;
        mArticoliGriglia.clear();
        mArticoliGriglia.addAll(articoliGriglia);
        this.refreshListArticoli();
    }

    public void refreshListArticoli() {
        refreshListArticoli(null);
    }

    public void refreshListArticoli(Runnable onSuccess) {
        this.sendOnLoadingStarted();
        this.loadArticoli(mCurrentOrdine, articoli -> {
            articoli = Stream.of(articoli).map(this::completeDataArticoli).sorted((a, b) -> a.getDataIns().after(b.getDataIns()) ? -1 : 1).toList();
            this.mArticoli.postValue(articoli);
            this.sendOnLoadingEnded();
            if (onSuccess != null)
                onSuccess.run();
        });
    }

    private ArticoloOrdine completeDataArticoli(ArticoloOrdine art) {
        ArticoloDTO artGrid = Stream.of(mArticoliGriglia).filter(x -> art.getCodMart().equalsIgnoreCase(x.getCodMart())).findFirstOrElse(new ArticoloDTO());
        art.setGgScadenza(artGrid.getGgScadenza());
        art.setGiacenza(BigDecimal.valueOf(artGrid.getGiacenza()));
        art.setQtaProposta(BigDecimal.valueOf(artGrid.getQtaProposta()));
        art.setQtaPrevistaVendita(BigDecimal.valueOf(artGrid.getQtaPrevistaVendita()));
        art.setBgTint(getBgTint(art));
        art.setMediaSett(artGrid.getMediaSett());
        art.setQtaMinOrdinabile(artGrid.getQtaMinOrdinabile());
        art.setMerceDaRic(artGrid.getMerceDaRic());
        art.setNewNoPromo(artGrid.isNewNoPromo());
        art.setFlagQtaMultipla(artGrid.getFlagQtaMultipla());
        art.setSystemNote(artGrid.generateSystemNote());
        return art;
    }

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

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

    public void processBarcode(String barcode) {
        try {
            this.sendOnLoadingStarted();

            ArticoloDTO articoloDTO = getArticoloFromBarcode(barcode);
            if (articoloDTO == null) {
                throw new NoResultFromBarcodeException(barcode);
            }
            this.addArticoloToOrdine(
                    articoloDTO,
                    this::sendOnLoadingEnded,
                    this::sendError
            );
        } catch (Exception e) {
            this.sendError(e);
            BarcodeManager.enable();
        } finally {
        }
    }

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

    public void addArticoloToOrdine(ArticoloDTO art, Runnable onSuccess, RunnableArgs<Exception> onFail) {
        try {

            mArticoliOrdineRepository.findArticoloByScanAndOrdine(mCurrentOrdine, art.codMart, foundArt -> {

                var articolo = foundArt != null ? foundArt : art.convertToArticoloOrdine(mCurrentOrdine);

                if (isAutoOrderOnScan()) {
                    articolo.setQtaOrd(articolo.getQtaOrd() + articolo.getQtaCnf());
                    mArticoliOrdineRepository.saveArticoloToOrdine(articolo, () -> this.refreshListArticoli(onSuccess), onFail);
                } else {
                    this.sendOnItemDispatched(articolo, onSuccess);
                }


            }, onFail);


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

    public ArticoloDTO getArticoloFromBarcode(String barcode) throws NoArtsInGridException {
        if (mArticoliGriglia == null || mArticoliGriglia.isEmpty()) {
            throw new NoArtsInGridException();
        }

        return Stream.of(mArticoliGriglia).filter(articolo ->
                articolo.getCodMart().equalsIgnoreCase(barcode) ||
                        articolo.getBarcode().equalsIgnoreCase(barcode) ||
                        articolo.getBarcode().equalsIgnoreCase(StringUtils.leftPad(barcode, 13, "0"))
        ).sortBy(x -> x.getSortByBarcodeCondition(barcode)).findFirst().orElse(null);
    }


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

    private List<ArticoloDTO> getNewArticoli() {
        return mArticoliGriglia.stream()
                .filter(x -> x.getQtaOrd() > 0f)
                .collect(Collectors.toList());
    }

    public void orderNewProducts() {
        List<ArticoloDTO> newArts = getNewArticoli();
        if (!newArts.isEmpty()) {
            List<ArticoloOrdine> articoliToSave = new ArrayList<>();

            Stream.of(newArts).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, this::refreshListArticoli, this::sendError);

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

    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(), (ordine) -> {

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

        }, this::sendError);
    }

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

    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, Runnable onSuccess);

        int getColorFromResource(int resId);

        void onError(Exception ex);
    }

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