package it.integry.integrywmsnative.gest.picking_libero.viewmodel;

import android.app.Activity;
import android.app.ProgressDialog;
import android.text.SpannableString;
import android.view.View;

import com.annimon.stream.Stream;
import com.google.android.material.bottomsheet.BottomSheetBehavior;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.databinding.Observable;
import androidx.databinding.ObservableArrayList;
import androidx.databinding.ObservableField;
import androidx.databinding.ObservableList;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.CommonConst;
import it.integry.integrywmsnative.core.REST.consumers.ArticoloRESTConsumer;
import it.integry.integrywmsnative.core.REST.consumers.BarcodeRESTConsumer;
import it.integry.integrywmsnative.core.REST.consumers.ColliMagazzinoRESTConsumer;
import it.integry.integrywmsnative.core.REST.consumers.ISimpleOperationCallback;
import it.integry.integrywmsnative.core.REST.model.Ean128Model;
import it.integry.integrywmsnative.core.REST.model.Ean13PesoModel;
import it.integry.integrywmsnative.core.barcode_reader.BarcodeManager;
import it.integry.integrywmsnative.core.di.BindableBoolean;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.interfaces.IRecyclerItemClicked;
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.model.secondary.GestioneEnum;
import it.integry.integrywmsnative.core.utility.UtilityBarcode;
import it.integry.integrywmsnative.core.utility.UtilityExceptions;
import it.integry.integrywmsnative.core.utility.UtilityNumber;
import it.integry.integrywmsnative.core.utility.UtilityProgress;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.databinding.ActivityPickingLiberoBinding;
import it.integry.integrywmsnative.gest.picking_libero.PickingLiberoActivity;
import it.integry.integrywmsnative.gest.picking_libero.core.PickingLiberoHelper;
import it.integry.integrywmsnative.gest.picking_libero.core.PickingLiberoListAdapter;
import it.integry.integrywmsnative.gest.vendita.dto.PickingObjectDTO;
import it.integry.integrywmsnative.ui.StatusBarAlert;
import it.integry.integrywmsnative.view.dialogs.DialogSimpleMessageHelper;
import it.integry.integrywmsnative.view.dialogs.choose_arts_from_lista_arts.DialogChooseArtsFromListaArts;
import it.integry.integrywmsnative.view.dialogs.input_quantity.DialogInputQuantity;
import it.integry.integrywmsnative.view.dialogs.input_quantity.QuantityDTO;
import it.integry.plugins.barcode_base_library.model.BarcodeScanDTO;

public class PickingLiberoViewModel implements IRecyclerItemClicked<MtbColr> {

    public ObservableField<MtbColt> mtbColt = new ObservableField<>();

    public BindableBoolean thereIsAnOpenedUL = new BindableBoolean();
    public BindableBoolean thereIsntAnOpenedUL = new BindableBoolean();
    public BindableBoolean thereIsAnyRowInUL = new BindableBoolean();
    public BindableBoolean thereIsAnOpenULWithoutRows = new BindableBoolean();

    private BottomSheetBehavior mBottomSheetBehavior;

    private PickingLiberoActivity mContext;
    private ActivityPickingLiberoBinding mBinding;
    private PickingLiberoHelper mHelper;

    private PickingLiberoListAdapter mAdapter;

    public void init(PickingLiberoActivity context, ActivityPickingLiberoBinding binding, PickingLiberoHelper helper) {
        mContext = context;
        mBinding = binding;
        mHelper = helper;

        initObservable();

        initRecyclerView();

        initBottomSheetActions();
    }


    private void initObservable() {
        refreshBindables();

        mtbColt.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
            @Override
            public void onPropertyChanged(Observable sender, int propertyId) {
                refreshBindables();
            }
        });
    }

    private void refreshBindables() {
        if(mtbColt.get() != null)  {
            initAdapter();

            mContext.setTitle(String.format(mContext.getText(R.string.lu_number_text).toString(), mtbColt.get().getNumCollo()));

            initObservableMtbColr();
        }
        else {
            destroyAdapter();

            mContext.setTitle(mContext.getText(R.string.free_picking).toString());
        }

        thereIsAnOpenedUL.set(mtbColt.get() != null);
        thereIsntAnOpenedUL.set(!thereIsAnOpenedUL.get());


        refreshObservableMtbColr();


    }

    private void initObservableMtbColr() {
        mtbColt.get().getMtbColr().addOnListChangedCallback(new ObservableList.OnListChangedCallback<ObservableList<MtbColr>>() {
            @Override
            public void onChanged(ObservableList<MtbColr> sender) {
                refresh();
            }

            @Override
            public void onItemRangeChanged(ObservableList<MtbColr> sender, int positionStart, int itemCount) {
                refresh();
            }

            @Override
            public void onItemRangeInserted(ObservableList<MtbColr> sender, int positionStart, int itemCount) {
                refresh();
            }

            @Override
            public void onItemRangeMoved(ObservableList<MtbColr> sender, int fromPosition, int toPosition, int itemCount) {
                refresh();
            }

            @Override
            public void onItemRangeRemoved(ObservableList<MtbColr> sender, int positionStart, int itemCount) {
                refresh();
            }

            private void refresh() {
                refreshObservableMtbColr();
            }
        });
    }

    private void refreshObservableMtbColr() {
        thereIsAnyRowInUL.set(mtbColt.get() != null &&
                mtbColt.get().getMtbColr() != null &&
                mtbColt.get().getMtbColr().size() > 0);


        thereIsAnOpenULWithoutRows.set(mtbColt.get() != null &&
                (mtbColt.get().getMtbColr() == null || mtbColt.get().getMtbColr().size() == 0));
    }


    private void initRecyclerView() {
        mBinding.pickingLiberoMainList.setNestedScrollingEnabled(false);

        mBinding.pickingLiberoMainList.setHasFixedSize(true);

        mBinding.pickingLiberoMainList.setLayoutManager(new LinearLayoutManager(mContext));

        DividerItemDecoration itemDecorator = new DividerItemDecoration(mContext, DividerItemDecoration.VERTICAL);
        itemDecorator.setDrawable(ContextCompat.getDrawable(mContext, R.drawable.divider));
        mBinding.pickingLiberoMainList.addItemDecoration(itemDecorator);
    }

    private void initAdapter() {
        mAdapter = new PickingLiberoListAdapter(mContext, mtbColt.get().getMtbColr(), this);
        mBinding.pickingLiberoMainList.setAdapter(mAdapter);
    }

    private void destroyAdapter() {
        mAdapter = null;
        mBinding.pickingLiberoMainList.setAdapter(null);
    }

    private void initBottomSheetActions() {
        mBinding.bg.setOnClickListener(v -> mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED));

        mBottomSheetBehavior = BottomSheetBehavior.from(mBinding.bottomSheetActions);

        mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                if (newState == BottomSheetBehavior.STATE_COLLAPSED)
                    mBinding.bg.setVisibility(View.GONE);
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                mBinding.bg.setVisibility(View.VISIBLE);
                mBinding.bg.setAlpha(slideOffset);
            }
        });
    }


    public void onBackPressed() {
        if(thereIsAnOpenedUL.get()) {
            this.closeLU();
        }
    }


    public RunnableArgs<BarcodeScanDTO> onScanSuccessfull = data -> {

        ProgressDialog progressDialog = UtilityProgress.createDefaultProgressDialog(mContext);
        BarcodeManager.disable();


        if(UtilityBarcode.isEtichettaAnonima(data)){
            //Cerco gli articoli presenti nell'ul dell'etichetta anonima
            this.executeEtichettaLU(data.getStringValue(), progressDialog);
        } else if(UtilityBarcode.isEtichetta128(data)) {

            //Cerco tramite etichetta ean 128 (che può indicarmi una UL)
            this.executeEtichettaEan128(data, progressDialog);
        } else {
            BarcodeManager.enable();
        }

    };


    private void executeEtichettaLU(String sscc, ProgressDialog progressDialog) {
        ColliMagazzinoRESTConsumer.getBySSCC(sscc, true, false, mtbColtScanned -> {

            if(mtbColtScanned == null) {
                showNoULFound(() -> {
                    BarcodeManager.enable();
                    progressDialog.dismiss();
                });
            } else if((mtbColtScanned.getGestioneEnum() == GestioneEnum.ACQUISTO || mtbColtScanned.getGestioneEnum() == GestioneEnum.LAVORAZIONE) && mtbColtScanned.getSegno() > 0) {
                pickMerceULtoUL(mtbColtScanned, mtbColt.get(), progressDialog);
            } else {
                showNoULFound(() -> {
                    BarcodeManager.enable();
                    progressDialog.dismiss();
                });
            }

        }, ex ->  {
            showNoULFound(() -> {
                BarcodeManager.enable();
                progressDialog.dismiss();
            });
        });
    }


    private void executeEtichettaEan128(BarcodeScanDTO barcodeScanDTO, ProgressDialog progressDialog) {
        BarcodeRESTConsumer.decodeEan128(barcodeScanDTO, ean128Model -> {

            String barcodeProd = null;

            if(!UtilityString.isNullOrEmpty(ean128Model.Sscc)) barcodeProd = ean128Model.Sscc;
            if(!UtilityString.isNullOrEmpty(ean128Model.Gtin)) barcodeProd = ean128Model.Gtin;
            if(!UtilityString.isNullOrEmpty(ean128Model.Content)) barcodeProd = ean128Model.Content;


            if(!UtilityString.isNullOrEmpty(barcodeProd)) {

                if(!UtilityString.isNullOrEmpty(ean128Model.Sscc)){
                    this.executeEtichettaLU(ean128Model.Sscc, progressDialog);
                } else {
                    showNoULFound(() -> {
                        BarcodeManager.enable();
                        progressDialog.dismiss();
                    });

                }


            } else {
                //EAN 128 non completo o comunque mancano i riferimenti al prodotto
                showNoULFound(() -> {
                    BarcodeManager.enable();
                    progressDialog.dismiss();
                });
            }
        }, ex-> {
            UtilityExceptions.defaultException(mContext, ex, progressDialog);
            BarcodeManager.enable();
        });
    }



    private void pickMerceULtoUL(MtbColt sourceMtbColt, MtbColt destMtbColt, ProgressDialog progressDialog) {
        List<MtbColr> mtbColrsToPick = Stream.of(sourceMtbColt.getMtbColr())
                .filter(x -> x.getQtaCol().floatValue() > 0)
                .toList();

        DialogChooseArtsFromListaArts.make(mContext, mtbColrsToPick, pickedAarts -> {

            progressDialog.dismiss();

            List<MtbColr> destNewMtbColr = new ArrayList<>();

            askQuantities(pickedAarts.iterator(), destNewMtbColr, () -> {

                if(destNewMtbColr != null && destNewMtbColr.size() > 0){
                    MtbColt clonedTestata = (MtbColt) destMtbColt.clone();
                    ObservableArrayList<MtbColr> mtbColrObservableField = new ObservableArrayList<>();

                    for(int i = 0; i < destNewMtbColr.size(); i++) {
                        MtbColr cloneMtbColr = (MtbColr) destNewMtbColr.get(i).clone();

                        cloneMtbColr
                                .setGestioneRif(cloneMtbColr.getGestione())
                                .setGestione(null);

                        cloneMtbColr
                                .setSerColloRif(cloneMtbColr.getSerCollo())
                                .setSerCollo(null);

                        cloneMtbColr
                                .setNumColloRif(cloneMtbColr.getNumCollo())
                                .setNumCollo(null);

                        cloneMtbColr
                                .setDataColloRif(cloneMtbColr.getDataColloS())
                                .setDataCollo(null);

                        mtbColrObservableField.add(cloneMtbColr);
                    }

                    clonedTestata.setMtbColr(mtbColrObservableField);

                    saveLU(clonedTestata);
                }

            });

        }, () -> {
            progressDialog.dismiss();
            BarcodeManager.enable();
        }).show();
    }


    private void askQuantities(Iterator<MtbColr> sourceMtbColrs, List<MtbColr> destMtbColr, Runnable onComplete){

        if(sourceMtbColrs.hasNext()){

            askSingleQuantity(sourceMtbColrs.next(), mtbColr -> {
                destMtbColr.add(mtbColr);
                askQuantities(sourceMtbColrs, destMtbColr, onComplete);
            });
        } else {
            onComplete.run();
        }
    }


    private void askSingleQuantity(MtbColr mtbColr, RunnableArgs<MtbColr> onComplete) {
        DialogInputQuantity.DTO dto = new DialogInputQuantity.DTO()
                .setBatchLot(mtbColr.getPartitaMag())
                .setDataScad(mtbColr.getDataScadPartitaD())
                .setMtbAart(mtbColr.getMtbAart())
                .setQtaTot(mtbColr.getQtaCol())
                .setQtaOrd(mtbColr.getQtaCol())
                .setQtaDaEvadere(new BigDecimal(-1))
                .setQtaEvasa(BigDecimal.ZERO)
                .setCanPartitaMagBeChanged(false)
                .setCanDataScadBeChanged(false)
                .setMaxQta(mtbColr.getQtaCol());

        DialogInputQuantity.makeBase(mContext, dto, false, quantityDTO -> {
            mtbColr
                    .setQtaCol(new BigDecimal(quantityDTO.qtaTot.get()))
                    .setQtaCnf(new BigDecimal(quantityDTO.qtaCnf.get()))
                    .setNumCnf(new BigDecimal(quantityDTO.numCnf.get()))
                    .setDatetimeRow(new Date());

            onComplete.run(mtbColr);
        }).show();
    }



    private void saveLU(MtbColt mtbColtToSave) {
        mtbColtToSave.setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

        for(int i = 0; i < mtbColtToSave.getMtbColr().size(); i++) {
            mtbColtToSave.getMtbColr().get(i).setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);
        }

        ProgressDialog progressDialog = UtilityProgress.createDefaultProgressDialog(mContext);

        ColliMagazzinoRESTConsumer.saveCollo(mtbColtToSave, value -> {
            mtbColt.get().getMtbColr().addAll(mtbColtToSave.getMtbColr());

            for(int i = 0; i < mtbColt.get().getMtbColr().size(); i++) {
                mtbColt.get().getMtbColr().get(i).setOperation(CommonModelConsts.OPERATION.NO_OP);
            }

            progressDialog.dismiss();
            BarcodeManager.enable();
        }, ex -> {
            BarcodeManager.enable();
            UtilityExceptions.defaultException(mContext, ex, progressDialog);
        });

    }


    private void showDataSavedDialog() {

        DialogSimpleMessageHelper.makeSuccessDialog(
                mContext,
                mContext.getResources().getString(R.string.completed),
                new SpannableString(mContext.getResources().getString(R.string.data_saved)),
                null, null).show();

    }


    private void showNoULFound(Runnable onComplete) {
        DialogSimpleMessageHelper.makeWarningDialog(mContext,
                new SpannableString(mContext.getResources().getText(R.string.no_lu_found_message)),
                null, onComplete).show();
    }





    public void createNewLU() {
        createNewLU(null, null, null);
    }

    private void createNewLU(Integer customNumCollo, String customSerCollo, Runnable onComplete) {

        ProgressDialog progressDialog = UtilityProgress.createDefaultProgressDialog(mContext);

        MtbColt mtbColt = new MtbColt();
        mtbColt .setGestione(GestioneEnum.VENDITA)
                .setSegno(-1)
                .setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

        if(customNumCollo != null) {
            mtbColt.setNumCollo(customNumCollo);
        }

        if(!UtilityString.isNullOrEmpty(customSerCollo)) {
            mtbColt.setSerCollo(customSerCollo);
        }

        ColliMagazzinoRESTConsumer.saveCollo(mtbColt, savedMtbColt -> {
            savedMtbColt.setMtbColr(new ObservableArrayList<>());
            setULToCurrentContext(savedMtbColt);

            progressDialog.dismiss();

            new StatusBarAlert.Builder(mContext)
                    .autoHide(true)
                    .withDuration(2500)
                    .showProgress(false)
                    .withText(R.string.data_saved)
                    .withAlertColor(R.color.mainGreen)
                    .build();

            if(onComplete != null) onComplete.run();

        }, ex -> UtilityExceptions.defaultException(mContext, ex, progressDialog));
    }


    private void setULToCurrentContext(MtbColt mtbColt){
        if(mtbColt != null &&  mtbColt.getMtbColr() == null) {
            mtbColt.setMtbColr(new ObservableArrayList<>());
        }
        this.mtbColt.set(mtbColt);
    }


    public void closeLU() {
        if(thereIsAnOpenedUL.get()) {
            final ProgressDialog progress = UtilityProgress.createDefaultProgressDialog(mContext);

            if(thereIsAnyRowInUL.get()) {
                updateDataFine(progress, null); //() -> distribuisciCollo(progress, () -> printCollo(progress)));
            } else {
                deleteCollo(progress);
            }

        }
    }

    private void updateDataFine(ProgressDialog progress, Runnable onComplete) {

        MtbColt cloneMtbColt = (MtbColt) this.mtbColt.get().clone();
        cloneMtbColt.setOperation(CommonModelConsts.OPERATION.NO_OP);
        cloneMtbColt.setOraFinePrep(new Date());


        ColliMagazzinoRESTConsumer.saveCollo(cloneMtbColt, new ISimpleOperationCallback<MtbColt>() {
            @Override
            public void onSuccess(MtbColt value) {
                if(onComplete != null) onComplete.run();
            }

            @Override
            public void onFailed(Exception ex) {
                UtilityExceptions.defaultException(mContext, ex, progress);
            }
        });

    }

    private void deleteCollo(ProgressDialog progress) {

        ColliMagazzinoRESTConsumer.deleteCollo(this.mtbColt.get(), () -> {
                    this.mtbColt.set(null);

                    progress.dismiss();
                }, ex -> UtilityExceptions.defaultException(mContext, ex, progress)
        );
    }

    @Override
    public void onItemClick(MtbColr item, int position) {
        mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);

        MtbColr clickedItem = mtbColt.get().getMtbColr().get(position);

        mBinding.bottomSheetActionsTitle.setText(clickedItem.getDescrizione());
        mBinding.bottomSheetActionsSubtitle.setText(clickedItem.getCodMart());

        mBinding.bottomSheetActionsDeleteBtn.setOnClickListener(v -> onItemDelete(item));

        mBinding.bottomSheetActionsQuantity.setText(UtilityNumber.decimalToString(clickedItem.getQtaCol()) +  " " + clickedItem.getMtbAart().getUntMis());
    }


    private void onItemDelete(MtbColr mtbColrToDelete) {
        mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

        MtbColr mtbColrToDeleteClone = (MtbColr) mtbColrToDelete.clone();

        mtbColrToDeleteClone
                .setRiga(null)
                .setDatetimeRow(new Date())
                .setQtaCol(mtbColrToDeleteClone.getQtaCol().multiply(new BigDecimal(-1)))
                .setOperation(CommonModelConsts.OPERATION.DELETE);

        MtbColt mtbColtClone = (MtbColt) mtbColt.get().clone();
        mtbColtClone.setMtbColr(new ObservableArrayList<>());
        mtbColtClone.getMtbColr().add(mtbColrToDeleteClone);
        mtbColtClone.setOperation(CommonModelConsts.OPERATION.INSERT_OR_UPDATE);

        ProgressDialog progressDialog = UtilityProgress.createDefaultProgressDialog(mContext);

        ColliMagazzinoRESTConsumer.saveCollo(mtbColtClone, value -> {
            progressDialog.dismiss();
            mtbColt.get().getMtbColr().remove(mtbColrToDelete);
        }, ex -> UtilityExceptions.defaultException(mContext, ex, progressDialog));
    }
}
