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

import androidx.lifecycle.MutableLiveData;

import com.annimon.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;

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

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.interfaces.viewmodel_listeners.ILoadingListener;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.ArtDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.ColloDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.GrigliaAcquistiChildDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.SaveDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.dto.TipiDocDTO;
import it.integry.integrywmsnative.gest.contab_doc_interni.rest.DocInterniRESTConsumer;
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.DialogInputQuantityV2ResultDTO;

public class DocInterniEditFormViewModel {

    @Inject
    MtbColrDataSource mtbColrRepository;
    @Inject
    MtbColtRepository mtbColtRepository;
    private final DocInterniRESTConsumer docInterniRESTConsumer;
    private DocInterniEditFormViewModel.Listener listener;
    private List<GrigliaAcquistiChildDTO> productsList;
    private TipiDocDTO tipoDoc;
    private boolean isCheckPartitaMag = false;
    private JSONObject checkFornitoreRules = null;
    public MutableLiveData<SqlMtbColt> document = 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() {
        try {
            var docInterniCheckFornitore = SettingsManager.iDB().getDocInterniCheckFornitore();
            this.checkFornitoreRules = docInterniCheckFornitore != null ? new JSONObject(docInterniCheckFornitore) : null;
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }


    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 editRow(SqlMtbColr row, boolean flagTracciabilita) {
        this.listener.onEditRowRequest(row, flagTracciabilita && this.isCheckPartitaMag);
    }

    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(), obj -> {
            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();
        docInterniRESTConsumer.saveDoc(this.getSaveDto(), obj -> {
            SqlMtbColt document = this.getDocument();
            document.setCodDtip(document.getCodDtipProvv());
            mtbColtRepository.update(document, doc -> {
                this.sendOnLoadingEnded();
                this.listener.onDocExported();
            }, this::sendError);
        }, this::sendError);
    }

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

    public void onSearch(String search) {
        GrigliaAcquistiChildDTO articolo = this.searchArticolo(search);
        if (articolo == null) {
            this.sendError(new NoResultFromBarcodeException());
            return;
        }
        List<SqlMtbColr> rows = this.getRowsForArticolo(articolo);
        if (rows.isEmpty()) {
            this.sendError(new NoResultFromBarcodeException());
        }
        if (rows.size() > 1) {
            this.listener.onMultipleRowsFound(rows, articolo);
        } else {
            this.editRow(rows.get(0), (articolo.getFlagTracciabilita().equalsIgnoreCase("S")));
        }

    }

    private List<SqlMtbColr> getRowsForArticolo(GrigliaAcquistiChildDTO articolo) {
        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(articolo.getBarcode());
        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 GrigliaAcquistiChildDTO searchArticolo(String filter) {
        return Stream.of(this.productsList).filter(dto ->
                filter.equalsIgnoreCase(dto.getCodMart())
                        || filter.equalsIgnoreCase(dto.getBarcode())
                        || StringUtils.leftPad(filter, 13, '0').equalsIgnoreCase(dto.getCodMart())
                        || StringUtils.leftPad(filter, 13, '0').equalsIgnoreCase(dto.getBarcode())
        ).findFirstOrElse(null);
    }

    private SaveDTO getSaveDto() {
        SqlMtbColt document = this.getDocument();
        SaveDTO saveDTO = new SaveDTO();
        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());
            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();
    }

    private void initCheckFornitore() {
        SqlMtbColt doc = this.document.getValue();
        boolean isCheckPartitaMag = false;
        if (doc != null && this.checkFornitoreRules != null) {
            String key = doc.getCodAnag();
            if (doc.getCodVdes() != null && !doc.getCodVdes().isEmpty()) {
                key += "-" + doc.getCodVdes();
            }
            try {
                if (this.checkFornitoreRules.has(key) && ((String) ((JSONObject) this.checkFornitoreRules.get(key)).get(doc.getCodDtipProvv())).equalsIgnoreCase("check-partitaMag")) {
                    isCheckPartitaMag = true;
                }
            } catch (Exception ignored) {
            }

        }
        this.isCheckPartitaMag = isCheckPartitaMag;
    }

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

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

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

    public void processBarcode(BarcodeScanDTO dto) {
        String code = dto.getStringValue();
        this.onSearch(code);
    }

    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());
        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 GrigliaAcquistiChildDTO getArticoloByCodMart(String codMart) {
        return Stream.of(productsList).filter(prod -> prod.getCodMart().equalsIgnoreCase(codMart)).findFirstOrElse(null);
    }

    public DocInterniEditFormViewModel setTipoDoc(TipiDocDTO 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);

        void onMultipleRowsFound(List<SqlMtbColr> rows, GrigliaAcquistiChildDTO articolo);

        void onDocumentHoldRequest();

        void onDocExported();
    }


}
