package it.integry.integrywmsnative.gest.contab_doc_interni.edit_form;

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.core.data_store.db.entity.SqlMtbColr;
import it.integry.integrywmsnative.core.data_store.db.entity.SqlMtbColt;
import it.integry.integrywmsnative.core.data_store.db.repository.MtbColrDataSource;
import it.integry.integrywmsnative.core.data_store.db.repository.MtbColtRepository;
import it.integry.integrywmsnative.core.exception.NoResultFromBarcodeException;
import it.integry.integrywmsnative.core.exception.NoResultFromCodMartException;
import it.integry.integrywmsnative.core.interfaces.viewmodel_listeners.ILoadingListener;
import it.integry.integrywmsnative.core.rest.model.Ean128Model;
import it.integry.integrywmsnative.core.rest.model.Ean13PesoModel;
import it.integry.integrywmsnative.core.rest.model.documento.DocumentoArtDTO;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityBarcode;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilitySHA1;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.ArtDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.CheckFornitoreDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.ColloDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.DocInterniSetupDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.FornitoreDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.SaveDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.TipoDocDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.rest.DocInterniRESTConsumer;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.DialogInputQuantityV2ResultDTO;

public class DocInterniEditFormViewModel {

    private final MtbColrDataSource mtbColrRepository;
    private final MtbColtRepository mtbColtRepository;
    private final DocInterniRESTConsumer docInterniRESTConsumer;
    private Listener listener;
    private List<DocumentoArtDTO> productsList;
    private TipoDocDTO tipoDoc;

    private boolean isCheckPartitaMag = false;


    public MutableLiveData<SqlMtbColt> document = new MutableLiveData<>();
    public MutableLiveData<DocInterniSetupDTO> docInterniSetupDTO = new MutableLiveData<>();
    public MutableLiveData<List<SqlMtbColr>> docRows = new MutableLiveData<>();

    @Inject
    public DocInterniEditFormViewModel(DocInterniRESTConsumer docInterniRESTConsumer, MtbColrDataSource mtbColrRepository, MtbColtRepository mtbColtRepository) {
        this.docInterniRESTConsumer = docInterniRESTConsumer;
        this.mtbColrRepository = mtbColrRepository;
        this.mtbColtRepository = mtbColtRepository;
        this.docRows.setValue(new ArrayList<>());
    }

    public void init() {
    }


    private void sendOnLoadingStarted() {
        if (this.listener != null) listener.onLoadingStarted();
    }

    private void sendOnLoadingEnded() {
        if (this.listener != null) listener.onLoadingEnded();
    }

    private void sendError(Exception ex) {
        if (this.listener != null) listener.onError(ex);
    }

    public void fetchDocumentRows() {
        this.sendOnLoadingStarted();
        mtbColrRepository.selectAllByDocumentID(this.document.getValue().getId(), rows -> {
            this.docRows.postValue(rows);
            this.listener.onRowsChanged(rows);
            this.sendOnLoadingEnded();
        }, this::sendError);


    }

    public void dispatchRowEdit(SqlMtbColr row, boolean flagTracciabilita, boolean isUntMisDig) {
        this.listener.onEditRowRequest(row, flagTracciabilita && this.isCheckPartitaMag, isUntMisDig);
    }

    private Integer getNextIdRiga() {
        List<SqlMtbColr> rows = this.docRows.getValue();
        if (rows == null || rows.size() <= 0) {
            return 1;
        }
        return Stream.of(rows).mapToInt(SqlMtbColr::getRiga).max().getAsInt() + 1;
    }

    public void exportDocument() {
        this.sendOnLoadingStarted();
        docInterniRESTConsumer.saveDoc(this.getSaveDto(), () -> {
            SqlMtbColt document = this.getDocument();
            document.setCodDtip(document.getCodDtipProvv());
            mtbColtRepository.update(document, doc -> {
                this.sendOnLoadingEnded();
                this.listener.onDocExported();
            }, this::sendError);
        }, this::sendError);
    }

    public void deleteDocument() {
        this.sendOnLoadingStarted();
        SqlMtbColt document = this.getDocument();
        mtbColtRepository.delete(document, () -> {
            this.sendOnLoadingEnded();
            this.listener.onDocDeleted();
        }, this::sendError);
    }

    public void holdDocument() {
        this.listener.onDocumentHoldRequest();
    }

    public void loadArticoloByCodMartOrBarcode(String codMart) {
        var matchedArt = Stream.of(productsList)
                .filter(x -> x.getCodMart().equalsIgnoreCase(codMart) ||
                        x.getBarcode().contains(codMart))
                .findFirst()
                .orElse(null);


        if (matchedArt == null) {
            this.sendError(new NoResultFromCodMartException(codMart));
            return;
        }


        loadArticolo(matchedArt, null);
    }

    public void loadArticoloByBarcode(String barcodeProd, Ean128Model ean128Model) {
        DocumentoArtDTO matchedArt;
        String usedBarcode = null;

        matchedArt = Stream.of(productsList)
                .filter(x -> x.getBarcode().contains(barcodeProd))
                .findFirst()
                .orElse(null);

        if (matchedArt != null)
            usedBarcode = matchedArt.getBarcode().contains(barcodeProd) ? barcodeProd : null;


        if (matchedArt == null) {
            this.sendError(new NoResultFromBarcodeException(barcodeProd));
            return;
        }

        loadArticolo(matchedArt, usedBarcode);
    }

    private void loadArticolo(DocumentoArtDTO documentoArtDTO, String usedBarcode) {
        List<SqlMtbColr> rows = this.getRowsForArticolo(documentoArtDTO, usedBarcode);

        if (rows.size() > 1) {
            this.listener.onMultipleRowsFound(rows, documentoArtDTO);
        } else {
            this.dispatchRowEdit(rows.get(0), documentoArtDTO.isFlagTracciabilita(), documentoArtDTO.isUntMisDigitale());
        }
    }

    private List<SqlMtbColr> getRowsForArticolo(DocumentoArtDTO articolo, String barcode) {
        List<SqlMtbColr> docRows = this.docRows.getValue();
        if (docRows == null) {
            docRows = new ArrayList<>();
        }
        SqlMtbColt document = this.document.getValue();
        SqlMtbColr row = new SqlMtbColr();

        row.setIdCollo(document.getId());
        row.setRiga(getNextIdRiga());
        row.setCodMart(articolo.getCodMart());
        row.setSerCollo(document.getSerCollo());
        row.setGestione(document.getGestione());
        row.setNumCollo(document.getNumCollo());
        row.setDataCollo(document.getDataCollo());
        row.setDescrizione(articolo.getDescrizione());
        row.setUntMis(articolo.getUntMis());
        row.setCodBarre(barcode);
        row.setQtaCnf(articolo.getQtaCnf().floatValue());
        row.setNumCnf(0f);
        row.setQtaCol(0f);

        List<SqlMtbColr> rows = Stream.of(docRows)
                .filter(docRow -> docRow.getCodMart().equalsIgnoreCase(articolo.getCodMart()))
                .toList();
        rows.add(row);
        return rows;
    }

    private SaveDTO getSaveDto() {
        SqlMtbColt document = this.getDocument();

        String UUID = SettingsManager.i().getUserSession().getDeviceId() + "-" + document.getId();
        UUID = UtilitySHA1.getSha1Hex(UUID);

        SaveDTO saveDTO = new SaveDTO();
        saveDTO.setUUID(UUID);
        saveDTO.setIdDisp("1");
        saveDTO.setGestione(document.getGestione());
        saveDTO.setCodMdep(document.getCodMdep());
        saveDTO.setCodDtip(document.getCodDtipProvv());
        saveDTO.setAnnotazioni(document.getAnnotazioni());
        saveDTO.setSegno(document.getSegno().toString());

        ColloDTO collo = new ColloDTO();
        collo.setIdDisp("1");
        collo.setGestione(document.getGestione());
        collo.setCodMdep(document.getCodMdep());
        collo.setCodDtip(document.getCodDtipProvv());
        collo.setCreatedDate(UtilityDate.formatDate(document.getDataCollo(), UtilityDate.COMMONS_DATE_FORMATS.DMY_TIME_SLASH));
        collo.setAnnotazioni(document.getAnnotazioni());
        collo.setSegno(saveDTO.getSegno());
        collo.setDataDoc(document.getDataDoc());
        collo.setNumDoc(document.getNumDoc());
        collo.setCodAnag(document.getCodAnag());
        collo.setCodVdes(document.getCodVdes());

        for (SqlMtbColr row : this.getDocumentRows()) {
            ArtDTO artDto = new ArtDTO();
            artDto.setBarcode(row.getCodBarre());
            artDto.setQta(BigDecimal.valueOf(row.getQtaCol()));
            artDto.setDataIns(row.getDataCollo());
            artDto.setColli(BigDecimal.valueOf(row.getNumCnf()));
            artDto.setUntMis(row.getUntMis());
            artDto.setCodMart(row.getCodMart());
            artDto.setSystemNote(row.getNote());
            artDto.setPartitaMag(row.getPartitaMag());
            if (row.getDataScad() != null) {
                artDto.setDataScad(row.getDataScad());
            }
            artDto.setDescrizione(row.getDescrizione());
            collo.addArtRow(artDto);
        }
        saveDTO.setColloDTO(collo);

        return saveDTO;
    }

    public DocInterniEditFormViewModel setListeners(Listener listener) {
        this.listener = listener;
        return this;
    }

    public void setDocument(SqlMtbColt document) {
        this.document.setValue(document);
        this.initCheckFornitore();
    }

    public void setDocInterniSetupDTO(DocInterniSetupDTO docInterniSetupDTO) {
        this.docInterniSetupDTO.setValue(docInterniSetupDTO);
    }

    private void initCheckFornitore() {
        SqlMtbColt doc = this.document.getValue();
        List<FornitoreDTO> fornitori = Objects.requireNonNull(this.docInterniSetupDTO.getValue()).getFornitori();
        List<TipoDocDTO> tipoDocDTO = Objects.requireNonNull(this.docInterniSetupDTO.getValue()).getTipiDoc();
        this.isCheckPartitaMag = false;

        if (doc == null || fornitori == null || tipoDocDTO == null)
            return;

        List<FornitoreDTO> filterFornitori = Stream.of(fornitori)
                .filter(x -> x.getCodAnag().equals(doc.getCodAnag()))
                .toList();
        List<CheckFornitoreDTO> checkFornitoreDTO;

        TipoDocDTO tipoDoc = Stream.of(tipoDocDTO)
                .filter(x -> x.getCodDtip().equals(doc.getCodDtipProvv()))
                .findFirst()
                .get();

        if (tipoDoc.isFlagChkTracciabilita()) {
            this.isCheckPartitaMag = tipoDoc.isFlagChkTracciabilita();
        } else {
            if (filterFornitori.isEmpty())
                return;

            if (doc.getCodVdes() == null) {
                checkFornitoreDTO = Stream.of(filterFornitori)
                        .filter(x -> x.getCodVdes() == null).findFirst().get().getCheckFornitoreDTO();
            } else {
                checkFornitoreDTO = Stream.of(filterFornitori)
                        .filterNot(x -> x.getCodVdes() == null)
                        .filter(x -> x.getCodVdes().equals(doc.getCodVdes())).findFirst().get().getCheckFornitoreDTO();
            }

            if (checkFornitoreDTO != null) {
                CheckFornitoreDTO flagCheck = Stream.of(checkFornitoreDTO)
                        .filter(x -> x.getCodDtip().equals(doc.getCodDtipProvv())).findFirstOrElse(null);

                if (flagCheck == null)
                    return;

                this.isCheckPartitaMag = flagCheck.getFlagCheckPartitaMag();
            }
        }
    }

    public SqlMtbColt getDocument() {
        return document.getValue();
    }

    public List<DocumentoArtDTO> getProductsList() {
        return productsList;
    }

    public void setProductsList(List<DocumentoArtDTO> productsList) {
        this.productsList = productsList;
    }

    public void processBarcode(BarcodeScanDTO dto) {
        this.processBarcodeDTO(dto);
    }

    public void processBarcodeDTO(BarcodeScanDTO barcodeScanDTO) {
        if (UtilityBarcode.isEanPeso(barcodeScanDTO)) {
            //Cerco tramite etichetta ean peso
            this.executeEtichettaEanPeso(barcodeScanDTO);

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


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

    public void saveRow(SqlMtbColr row, DialogInputQuantityV2ResultDTO resultDTO) {
        row.setQtaCnf(resultDTO.getQtaCnf().floatValue());
        row.setNumCnf(resultDTO.getNumCnf().floatValue());
        row.setQtaCol(resultDTO.getQtaTot().floatValue());
        row.setNote(resultDTO.getNote());
        row.setDataScad(resultDTO.getDataScad());
        row.setPartitaMag(resultDTO.getPartitaMag());
        if (row.getId() > 0) {
            this.mtbColrRepository.update(row, id -> {
                this.fetchDocumentRows();
            }, this::sendError);
        } else {
            this.mtbColrRepository.insert(row, id -> {
                this.fetchDocumentRows();
            }, this::sendError);
        }
    }

    public void deleteRow(SqlMtbColr sqlMtbColr) {
        mtbColrRepository.delete(sqlMtbColr, this::fetchDocumentRows, this::sendError);
    }

    public void setNote(String note) {
        SqlMtbColt document = this.document.getValue();
        document.setAnnotazioni(note);
        mtbColtRepository.update(document, doc -> this.document.postValue(doc), this::sendError);
    }

    public List<SqlMtbColr> getDocumentRows() {
        return this.docRows.getValue();
    }

    public DocumentoArtDTO getArticoloByCodMart(String codMart) {
        return Stream.of(productsList)
                .filter(prod -> prod.getCodMart().equalsIgnoreCase(codMart))
                .findFirstOrElse(null);
    }

    public DocInterniEditFormViewModel setTipoDoc(TipoDocDTO docType) {
        this.tipoDoc = docType;
        return this;
    }

    public boolean isNotesMandatory() {
        return this.tipoDoc != null && this.tipoDoc.isRequireNote();
    }

    public interface Listener extends ILoadingListener {

        void onError(Exception ex);

        void onRowsChanged(List<SqlMtbColr> rows);

        void onEditRowRequest(SqlMtbColr row, boolean flagTracciabilita, boolean isUntMisDig);

        void onMultipleRowsFound(List<SqlMtbColr> rows, DocumentoArtDTO art);

        void onDocumentHoldRequest();

        void onDocDeleted();

        void onDocExported();
    }


}
