package it.integry.integrywmsnative.gest.inventario.picking;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;

import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;

import com.annimon.stream.Stream;
import com.ravikoradiya.liveadapter.LiveAdapter;
import com.ravikoradiya.liveadapter.Type;

import java.math.BigDecimal;
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.BR;
import it.integry.integrywmsnative.MainApplication;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.barcode_reader.BarcodeCallbackDTO;
import it.integry.integrywmsnative.core.barcode_reader.BarcodeManager;
import it.integry.integrywmsnative.core.data_cache.DataCache;
import it.integry.integrywmsnative.core.data_store.db.entity.InventarioRoomDTO;
import it.integry.integrywmsnative.core.data_store.db.entity.InventarioRowRoomDTO;
import it.integry.integrywmsnative.core.expansion.BaseActivity;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
import it.integry.integrywmsnative.core.model.MtbAart;
import it.integry.integrywmsnative.core.rest.model.inventario.InventarioArtDTO;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityExceptions;
import it.integry.integrywmsnative.databinding.ActivityPickingInventarioBinding;
import it.integry.integrywmsnative.databinding.ActivityPickingInventarioListItemBinding;
import it.integry.integrywmsnative.gest.contab_doc_interni.edit_form.dialog.selectDocRows.DialogSelectDocRowsItemListModel;
import it.integry.integrywmsnative.gest.contab_doc_interni.edit_form.dialog.selectDocRows.DialogSelectDocRowsView;
import it.integry.integrywmsnative.gest.inventario.bottom_sheet__inventario_row_actions.BottomSheetInventarioRowActionsView;
import it.integry.integrywmsnative.gest.spedizione.model.PickedQuantityDTO;
import it.integry.integrywmsnative.gest.spedizione.model.PickingObjectDTO;
import it.integry.integrywmsnative.view.dialogs.DialogConsts;
import it.integry.integrywmsnative.view.dialogs.available_items.DialogAvailableItemListModel;
import it.integry.integrywmsnative.view.dialogs.available_items.DialogAvailableItemsView;
import it.integry.integrywmsnative.view.dialogs.base.DialogSimpleInputHelper;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.DialogInputQuantityV2DTO;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.DialogInputQuantityV2View;
import it.integry.integrywmsnative.view.dialogs.yes_no.DialogYesNoView;

public class PickingInventarioActivity extends BaseActivity implements PickingInventarioViewModel.Listener {

    @Inject
    PickingInventarioViewModel mViewModel;

    @Inject
    DialogInputQuantityV2View mDialogInputQuantityV2View;

    private ActivityPickingInventarioBinding mBindings;

    private static final String KEY_INVENTARIO = "key_inventario";
    private static final String KEY_INVENTARIO_ARTS = "key_inventario_arts";
    private int barcodeScannerIstanceID;

    //Pass here all external parameters
    public static void startActivity(Context context, InventarioRoomDTO inventarioRoomDTO, List<InventarioArtDTO> inventarioArts) {
        Intent myIntent = new Intent(context, PickingInventarioActivity.class);

        String keyExtraItem = DataCache.addItem(inventarioRoomDTO);
        myIntent.putExtra(KEY_INVENTARIO, keyExtraItem);

        String keyExtraArtsItem = DataCache.addItem(inventarioArts);
        myIntent.putExtra(KEY_INVENTARIO_ARTS, keyExtraArtsItem);

        context.startActivity(myIntent);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.picking_inventario_menu, menu);
        return true;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mBindings = DataBindingUtil.setContentView(this, R.layout.activity_picking_inventario);
        mBindings.setLifecycleOwner(this);
        mBindings.setView(this);

        MainApplication.appComponent
                .pickingInventarioComponent()
                .create()
                .inject(this);

        mViewModel.setListener(this);
        mBindings.setViewmodel(mViewModel);

        mViewModel.init(
                DataCache.retrieveItem(getIntent().getStringExtra(KEY_INVENTARIO)),
                DataCache.retrieveItem(getIntent().getStringExtra(KEY_INVENTARIO_ARTS)),
                SettingsManager.iDB().isFlagCanAddUnknownItemsInventario(),
                SettingsManager.iDB().isFlagShouldAskToCreateOrUpdateRowInventario());

        initToolbar();
        initBarcodeReader();
    }


    @Override
    protected void onStart() {
        super.onStart();

        initRecyclerView();
    }

    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }

    @Override
    public void onBackPressed() {
        BarcodeManager.removeCallback(barcodeScannerIstanceID);
        super.onBackPressed();
    }

    private void initToolbar() {
        setSupportActionBar(mBindings.toolbar);

        mBindings.toolbar.setOnMenuItemClickListener(menuItem -> {
            if (menuItem.getItemId() == R.id.export) {
                requestExportConfirm();
                return true;
            } else if (menuItem.getItemId() == R.id.search_art) {
                manualSearch();
                return true;
            }

            return false;
        });
    }

    private void initRecyclerView() {
        mViewModel.getCurrentInventarioRows().observe(this, data -> {
//            binding.reportEmptyView.setVisibility(data == null || data.isEmpty() ? View.VISIBLE : View.GONE);
        });

        var itemType = new Type<InventarioRowRoomDTO, ActivityPickingInventarioListItemBinding>(R.layout.activity_picking_inventario__list_item, BR.item);
        itemType.areItemSame((oldItem, newItem) -> Objects.equals(oldItem.getId(), newItem.getId()));
        itemType.areContentsTheSame(InventarioRowRoomDTO::equals);
        itemType.onClick(x -> {
            new BottomSheetInventarioRowActionsView(x.getBinding().getItem())
                    .setListener(new BottomSheetInventarioRowActionsView.Listener() {
                        @Override
                        public void onItemEdit() {
                            updateItem(x.getBinding().getItem());
                        }

                        @Override
                        public void onItemDelete() {
                            deleteItemRequest(x.getBinding().getItem());
                        }
                    })
                    .show(getSupportFragmentManager(), "tag");

            return null;
        });

        new LiveAdapter(mViewModel.getCurrentInventarioRows(), this)
                .map(InventarioRowRoomDTO.class, itemType)
                .into(this.mBindings.pickingInventarioMainList);
    }

    private void initBarcodeReader() {
        barcodeScannerIstanceID = BarcodeManager.addCallback(new BarcodeCallbackDTO()
                .setOnScanSuccessful(onScanSuccessful)
                .setOnScanFailed(ex -> UtilityExceptions.defaultException(this, ex, false)));
    }

    private final RunnableArgs<BarcodeScanDTO> onScanSuccessful = data -> {
        this.mViewModel.processBarcodeDTO(data, this::onLoadingEnded);
    };


    private void updateItem(InventarioRowRoomDTO itemToUpdate) {
        mViewModel.updateRow(itemToUpdate);
    }


    private void deleteItemRequest(InventarioRowRoomDTO itemToDelete) {
        DialogYesNoView.newInstance("Eliminazione", "Vuoi davvero eliminare l'articolo?", result -> {
            if (result == DialogConsts.Results.YES) {
                mViewModel.deleteRow(itemToDelete);
            }
        }).show(getSupportFragmentManager(), "tag");
    }

    public void requestExportConfirm() {
        DialogYesNoView.newInstance("Esporta", "Vuoi confermare e chiudere l'inventario?", result -> {
                    if (result == DialogConsts.Results.YES)
                        mViewModel.exportAll(this::onBackPressed);
                })
                .show(getSupportFragmentManager(), "tag");
    }

    public void manualSearch() {
        BarcodeManager.disable();
        DialogSimpleInputHelper.makeInputDialog(this,
                        "Ricerca articolo",
                        null,
                        "Cod articolo / Barcode",
                        barcodeProd ->
                                this.mViewModel.loadArticoloByCodMartOrBarcode(barcodeProd, BarcodeManager::enable),
                        BarcodeManager::enable)
                .show();
    }

    @Override
    public void onItemDispatched(PickingObjectDTO pickingObjectDTO, MtbAart mtbAart, BigDecimal initialNumCnf, BigDecimal initialQtaCnf, BigDecimal initialQtaTot, BigDecimal totalQtaAvailable, BigDecimal totalNumCnfAvailable, BigDecimal qtaCnfAvailable, BigDecimal totalQtaToBeTaken, BigDecimal totalNumCnfToBeTaken, BigDecimal qtaCnfToBeTaken, boolean canOverflowOrderQuantity, boolean canPartitaMagBeChanged, boolean canLUBeClosed, RunnableArgss<PickedQuantityDTO, Boolean> onComplete) {
        DialogInputQuantityV2DTO dialogInputQuantityV2DTO = new DialogInputQuantityV2DTO()
                .setMtbAart(mtbAart)
                .setInitialNumCnf(initialNumCnf)
                .setInitialQtaCnf(initialQtaCnf)
                .setInitialQtaTot(initialQtaTot)
                .setTotalQtaAvailable(totalQtaAvailable)
                .setTotalNumCnfAvailable(totalNumCnfAvailable)
                .setQtaCnfAvailable(qtaCnfAvailable)
                .setTotalQtaToBeTaken(totalQtaToBeTaken)
                .setTotalNumCnfToBeTaken(totalNumCnfToBeTaken)
                .setQtaCnfToBeTaken(qtaCnfToBeTaken)
                .setCanInputZeroQuantity(true)
                .setCanOverflowOrderQuantity(canOverflowOrderQuantity)
                .setCanPartitaMagBeChanged(canPartitaMagBeChanged)
                .setCanLUBeClosed(canLUBeClosed)
                .setNumCnfEditable(false);

        if (!mDialogInputQuantityV2View.isVisible())
            mDialogInputQuantityV2View.setDialogInputQuantityV2DTO(dialogInputQuantityV2DTO)
                    .setOnComplete((resultDTO, shouldCloseLU) -> {
                        PickedQuantityDTO pickedQuantityDTO = new PickedQuantityDTO()
                                .setNumCnf(resultDTO.getNumCnf())
                                .setQtaCnf(resultDTO.getQtaCnf())
                                .setQtaTot(resultDTO.getQtaTot())
                                .setPartitaMag(resultDTO.getPartitaMag())
                                .setDataScad(resultDTO.getDataScad());

                        onComplete.run(pickedQuantityDTO, shouldCloseLU);
                    })
                    .show(getSupportFragmentManager(), "tag");
    }

    @Override
    public void onItemAlreadyFound(InventarioArtDTO matchedArt, List<InventarioRowRoomDTO> alreadyScannedItems) {
        final List<DialogSelectDocRowsItemListModel<Object>> dialogSelectDocRowsItemListModels = Stream.of(alreadyScannedItems)
                .map(x -> new DialogSelectDocRowsItemListModel<>()
                        .setBarcode(x.getScanCodBarre())
                        .setCodMart(x.getCodMart())
                        .setDescrizione(x.getDescrizione())
                        .setQtaOrd(x.getQta())
                        .setUntMisOrd(x.getUntMis())
                        .setNew(false)
                        .setOriginalModel(x))
                .toList();

        dialogSelectDocRowsItemListModels.add(new DialogSelectDocRowsItemListModel<>()
                .setCodMart(matchedArt.getCodMart())
                .setDescrizione(matchedArt.getDescrizione())
                .setQtaOrd(BigDecimal.ZERO)
                .setUntMisOrd(matchedArt.getUntMis())
                .setNew(true));

        DialogSelectDocRowsView.newInstance(dialogSelectDocRowsItemListModels, data -> {
            if (data.isNew())
                this.mViewModel.dispatchRowInsert(null, matchedArt, null);
            else
                this.mViewModel.dispatchRowEdit(matchedArt.toMtbAart(), (InventarioRowRoomDTO) data.getOriginalModel());
        }, () -> {

        }).show(this.getSupportFragmentManager(), "DialogSelectDocRowsView");
    }

    public void showAvailableArts() {
        var listaMtbAart = Stream.of(this.mViewModel.getAvailableArts())
                .map(InventarioArtDTO::toMtbAart)
                .map(DialogAvailableItemListModel::fromMtbAart)
                .toList();

        for (var inventoryItem : mViewModel.currentInventarioRows.getValue()) {
            var codMart = inventoryItem.getCodMart();

            Stream.of(listaMtbAart)
                    .filter(x -> x.getCodMart().equalsIgnoreCase(codMart))
                    .forEach(x -> x.setAggiunto(true));
        }


        new DialogAvailableItemsView(listaMtbAart, mtbAartClicked -> this.mViewModel.loadArticoloByMtbAart(mtbAartClicked, null))
                .show(getSupportFragmentManager(), "DialogAvailableInventarioItemsView");
    }
}