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

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

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

import it.integry.barcode_base_android_library.model.BarcodeScanDTO;
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.data_store.db.respository_new.InventarioRepository;
import it.integry.integrywmsnative.core.data_store.db.respository_new.InventarioRowRepository;
import it.integry.integrywmsnative.core.exception.NoResultFromBarcodeException;
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
import it.integry.integrywmsnative.core.interfaces.viewmodel_listeners.ILoadingListener;
import it.integry.integrywmsnative.core.model.MtbAart;
import it.integry.integrywmsnative.core.model.dto.PickDataDTO;
import it.integry.integrywmsnative.core.rest.model.Ean128Model;
import it.integry.integrywmsnative.core.rest.model.Ean13PesoModel;
import it.integry.integrywmsnative.core.rest.model.inventario.InventarioArtDTO;
import it.integry.integrywmsnative.core.utility.UtilityBarcode;
import it.integry.integrywmsnative.core.utility.UtilityBigDecimal;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityLiveData;
import it.integry.integrywmsnative.gest.spedizione.exceptions.InvalidPesoKGException;
import it.integry.integrywmsnative.gest.spedizione.model.PickedQuantityDTO;
import it.integry.integrywmsnative.gest.spedizione.model.PickingObjectDTO;

public class PickingInventarioViewModel extends ViewModel {


    private final InventarioRepository inventarioRepository;
    private final InventarioRowRepository inventarioRowRepository;

    public MutableLiveData<InventarioRoomDTO> currentInventario = new MutableLiveData<>();
    public LiveData<List<InventarioRowRoomDTO>> currentInventarioRows = new MutableLiveData<>();

    private List<InventarioArtDTO> availableInventarioArts;
    private Listener mListener;

    public PickingInventarioViewModel(
            InventarioRepository inventarioRepository,
            InventarioRowRepository inventarioRowRepository) {
        this.inventarioRepository = inventarioRepository;
        this.inventarioRowRepository = inventarioRowRepository;
    }

    public void init(InventarioRoomDTO inventarioRoomDTO, List<InventarioArtDTO> inventarioArts) {
        this.sendOnLoadingStarted();

        this.currentInventario.postValue(inventarioRoomDTO);
        this.currentInventarioRows = inventarioRowRepository.retrieve(inventarioRoomDTO.getId());
        this.availableInventarioArts = inventarioArts == null ? new ArrayList<>() : inventarioArts;

        UtilityLiveData.observeOnce(this.currentInventarioRows, data -> {
            this.sendOnLoadingEnded();
        });

        this.sendOnLoadingEnded();
    }

    public LiveData<InventarioRoomDTO> getCurrentInventario() {
        return currentInventario;
    }

    public LiveData<List<InventarioRowRoomDTO>> getCurrentInventarioRows() {
        return currentInventarioRows;
    }

    public List<InventarioArtDTO> getAvailableArts() {
        return availableInventarioArts;
    }

    public void processBarcodeDTO(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        if (UtilityBarcode.isEanPeso(barcodeScanDTO)) {
            //Cerco tramite etichetta ean 128 (che può indicarmi un articolo o una UL)
            this.executeEtichettaEanPeso(barcodeScanDTO, onComplete);

        } else {
            this.loadArticolo(barcodeScanDTO.getStringValue(), null, onComplete);
        }
    }

    public void updateRow(InventarioRowRoomDTO itemToUpdate) {
        var barcodeList = new ArrayList<String>();
        barcodeList.add(itemToUpdate.getScanCodBarre());

        var matchedArt = availableInventarioArts.stream()
                .filter(x -> itemToUpdate.getCodMart() != null && x.getCodMart().contains(itemToUpdate.getCodMart()))
                .findFirst()
                .orElse(new InventarioArtDTO()
                        .setBarcode(barcodeList)
                        .setFlagQtaCnfFissa(true)
                        .setPlu(false)
                        .setUntMis(itemToUpdate.getUntMis())
                        .setQtaCnf(itemToUpdate.getQtaConf()));

        var articolo = matchedArt.toMtbAart();
        articolo.setFlagTracciabilita("N");
        dispatchRowEdit(articolo, itemToUpdate);
    }

    public void deleteRow(InventarioRowRoomDTO itemToDelete) {
        this.inventarioRowRepository.delete(itemToDelete, () -> {
        }, this::sendError);
    }


    public void exportAll(Runnable onComplete) {
        this.sendOnLoadingStarted();
        currentInventario.getValue().setInventarioRowList(this.currentInventarioRows.getValue());

        this.inventarioRepository.export(currentInventario.getValue(), () -> {
            this.sendOnLoadingEnded();
            onComplete.run();
        }, this::sendError);
    }


    private void executeEtichettaEanPeso(BarcodeScanDTO barcodeScanDTO, Runnable onComplete) {
        try {
            Ean13PesoModel ean13PesoModel = Ean13PesoModel.fromBarcode(barcodeScanDTO.getStringValue());
            this.loadArticolo(ean13PesoModel.getPrecode(), ean13PesoModel.toEan128(), onComplete);
        } catch (Exception ex) {
            this.sendError(ex);
        }
    }

    private void loadArticolo(String barcodeProd, Ean128Model ean128Model, Runnable onComplete) {
        var barcodeList = new ArrayList<String>();
        barcodeList.add(barcodeProd);

        var matchedArt = availableInventarioArts.stream()
                .filter(x -> x.getBarcode().contains(barcodeProd))
                .findFirst()
                .orElse(new InventarioArtDTO()
                        .setBarcode(barcodeList)
                        .setFlagQtaCnfFissa(true)
                        .setPlu(false)
                        .setUntMis("PZ")
                        .setQtaCnf(BigDecimal.ONE));

        if (matchedArt != null) {
            this.dispatchRowInsert(barcodeProd, matchedArt, ean128Model);
            onComplete.run();
        } else
            this.sendError(new NoResultFromBarcodeException(barcodeProd));

    }


    private void dispatchRowInsert(String barcode, InventarioArtDTO inventarioArtDTO, Ean128Model ean128Model) {
        var mtbAart = inventarioArtDTO.toMtbAart();
        mtbAart.setFlagTracciabilita("N");

        PickingObjectDTO pickingObjectDTO = new PickingObjectDTO()
                .setMtbAart(mtbAart)
                .setTempPickData(PickDataDTO.fromEan128(ean128Model));

        BigDecimal numCnfDaPrelevare = null;
        BigDecimal qtaColDaPrelevare = null;
        BigDecimal qtaCnfDaPrelevare = null;

        BigDecimal initialNumCnf;
        BigDecimal initialQtaCnf;
        BigDecimal initialQtaTot;

        if (pickingObjectDTO.getTempPickData() != null && pickingObjectDTO.getTempPickData().getManualPickDTO() != null) {
            //Oppure le info del barcode scansionato
            PickDataDTO.ManualPickDTO manualPickDTO = pickingObjectDTO.getTempPickData().getManualPickDTO();

            qtaColDaPrelevare = manualPickDTO.getQtaTot();
            numCnfDaPrelevare = manualPickDTO.getNumCnf();

            if (inventarioArtDTO.isPlu()) {
//                if(inventarioArtDTO.getPrzVendIva().equals(BigDecimal.ZERO))
//                    this.sendError();

                qtaColDaPrelevare = UtilityBigDecimal.multiply(qtaColDaPrelevare, BigDecimal.TEN);
                qtaColDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, inventarioArtDTO.getPrzVendIva(), RoundingMode.HALF_UP);
            }

            if (mtbAart.isFlagQtaCnfFissaBoolean()) {
                qtaCnfDaPrelevare = mtbAart.getQtaCnf();

                if (manualPickDTO.isEanPeso()) {
                    if (mtbAart.getUntMisRifPeso() == MtbAart.UntMisRifPesoEnum.M) {
                        if (UtilityBigDecimal.equalsTo(mtbAart.getPesoKg(), BigDecimal.ZERO)) {
                            this.sendError(new InvalidPesoKGException());
                        } else {
                            qtaColDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, mtbAart.getPesoKg());
                        }
                    }
                }

            } else if (qtaColDaPrelevare != null && numCnfDaPrelevare != null) {
                qtaCnfDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, numCnfDaPrelevare);
            }


            if (qtaColDaPrelevare != null && numCnfDaPrelevare == null) {
                if (!mtbAart.isFlagQtaCnfFissaBoolean()) {
                    numCnfDaPrelevare = UtilityBigDecimal.divideAndRoundToInteger(qtaColDaPrelevare, mtbAart.getQtaCnf());
                    if (UtilityBigDecimal.equalsTo(numCnfDaPrelevare, BigDecimal.ZERO))
                        numCnfDaPrelevare = BigDecimal.ONE;
                    qtaCnfDaPrelevare = UtilityBigDecimal.divide(qtaColDaPrelevare, numCnfDaPrelevare);
                } else {
                    numCnfDaPrelevare = UtilityBigDecimal.divideToInteger(qtaColDaPrelevare, mtbAart.getQtaCnf());
                }

            } else if (numCnfDaPrelevare != null && qtaColDaPrelevare == null) {
                qtaCnfDaPrelevare = mtbAart.getQtaCnf();
                qtaColDaPrelevare = UtilityBigDecimal.multiply(numCnfDaPrelevare, qtaCnfDaPrelevare);
            }

            initialNumCnf = numCnfDaPrelevare;
            initialQtaCnf = qtaCnfDaPrelevare;
            initialQtaTot = qtaColDaPrelevare;
        } else {
            initialNumCnf = BigDecimal.ONE;
            initialQtaCnf = mtbAart.getQtaCnf();
            initialQtaTot = UtilityBigDecimal.multiply(initialNumCnf, initialQtaCnf);
        }

        if (numCnfDaPrelevare == null && qtaColDaPrelevare == null && initialNumCnf == null && initialQtaTot == null) {
            initialNumCnf = BigDecimal.ONE;
            initialQtaCnf = mtbAart.getQtaCnf();
            initialQtaTot = UtilityBigDecimal.multiply(initialNumCnf, initialQtaCnf);
        }

        if (inventarioArtDTO.isPlu()) {
            this.saveNewRow(pickingObjectDTO,
                    initialNumCnf,
                    initialQtaCnf,
                    initialQtaTot,
                    null,
                    barcode);
        } else {

            this.sendOnItemDispatched(
                    pickingObjectDTO,
                    pickingObjectDTO.getMtbAart(),
                    null, //initialNumCnf,
                    initialQtaCnf,
                    null, //initialQtaTot,
                    null,
                    null,
                    null,
                    null,
                    null,
                    null,
                    false,
                    true,
                    false,
                    (pickedQuantityDTO, shouldCloseLU) -> {
                        this.saveNewRow(pickingObjectDTO,
                                pickedQuantityDTO.getNumCnf(),
                                pickedQuantityDTO.getQtaCnf(),
                                pickedQuantityDTO.getQtaTot(),
                                pickedQuantityDTO.getPartitaMag(),
                                barcode);
                    });
        }
    }

    private void dispatchRowEdit(MtbAart mtbAart, InventarioRowRoomDTO inventarioRowRoomDTO) {

        final PickingObjectDTO pickingObjectDTO = new PickingObjectDTO()
                .setMtbAart(mtbAart);


        this.sendOnItemDispatched(
                pickingObjectDTO,
                pickingObjectDTO.getMtbAart(),
                inventarioRowRoomDTO.getNumConf(),
                inventarioRowRoomDTO.getQtaConf(),
                inventarioRowRoomDTO.getQta(),
                null, null, null,
                null, null, null,
                false,
                false,
                false,
                (pickedQuantityDTO, shouldCloseLU) -> {

                    this.saveEditedRow(inventarioRowRoomDTO,
                            pickedQuantityDTO.getNumCnf(),
                            pickedQuantityDTO.getQtaCnf(),
                            pickedQuantityDTO.getQtaTot(),
                            pickedQuantityDTO.getPartitaMag());
                });

    }


    private void saveNewRow(PickingObjectDTO pickingObjectDTO, BigDecimal numCnf, BigDecimal qtaCnf, BigDecimal qtaTot, String partitaMag, String scanCodBarre) {
        final InventarioRowRoomDTO inventarioRow = new InventarioRowRoomDTO();
        inventarioRow.setCodMart(pickingObjectDTO.getMtbAart().getCodMart());
        inventarioRow.setDescrizione(pickingObjectDTO.getMtbAart().getDescrizioneEstesa());
        inventarioRow.setPartitaMag(partitaMag);
        inventarioRow.setQta(qtaTot);
        inventarioRow.setNumConf(numCnf);
        inventarioRow.setQtaConf(qtaCnf);
        inventarioRow.setUntMis(pickingObjectDTO.getMtbAart().getUntMis());
        inventarioRow.setDataOraInv(UtilityDate.getNowTime());
        inventarioRow.setZona(currentInventario.getValue().getZona());
        inventarioRow.setParentId(currentInventario.getValue().getId());
        inventarioRow.setScanCodBarre(scanCodBarre);

        inventarioRowRepository.insert(inventarioRow, () -> {
        }, this::sendError);
    }

    private void saveEditedRow(InventarioRowRoomDTO inventarioRowRoomDTO, BigDecimal numCnf, BigDecimal qtaCnf, BigDecimal qtaTot, String partitaMag) {
        inventarioRowRoomDTO.setPartitaMag(partitaMag);
        inventarioRowRoomDTO.setQta(qtaTot);
        inventarioRowRoomDTO.setNumConf(numCnf);
        inventarioRowRoomDTO.setQtaConf(qtaCnf);
        inventarioRowRoomDTO.setDataOraInv(UtilityDate.getNowTime());

        inventarioRowRepository.update(inventarioRowRoomDTO, () -> {
        }, this::sendError);
    }


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

    private void sendOnItemDispatched(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) {
        if (this.mListener != null) mListener.onItemDispatched(pickingObjectDTO,
                mtbAart,
                initialNumCnf,
                initialQtaCnf,
                initialQtaTot,
                totalQtaAvailable,
                totalNumCnfAvailable,
                qtaCnfAvailable,
                totalQtaToBeTaken,
                totalNumCnfToBeTaken,
                qtaCnfToBeTaken,
                canOverflowOrderQuantity,
                canPartitaMagBeChanged,
                canLUBeClosed,
                onComplete);
    }

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

    public interface Listener extends ILoadingListener {

        void onError(Exception ex);

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


    }
}