package it.integry.integrywmsnative.core.settings;

import android.content.Context;

import com.annimon.stream.Stream;
import com.google.firebase.perf.metrics.Trace;

import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.model.StbGestSetup;
import it.integry.integrywmsnative.core.rest.consumers.GestSetupRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.PosizioniRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.SystemRESTConsumer;
import it.integry.integrywmsnative.core.rest.model.AvailableCodMdepsDTO;
import it.integry.integrywmsnative.core.utility.UtilityFirebase;

public class SettingsManager {

    private static String TAG = "SETTINGS";

    private static SettingsModel settingsModelIstance;
    private static DBSettingsModel dbSettingsModelIstance;

    private static boolean firstStart = false;

    private static Context mContext;

    public static void init(Context context) {
        mContext = context;

        settingsModelIstance = (SettingsModel) Stash.getObject(TAG, SettingsModel.class);

        if (settingsModelIstance == null) {
            settingsModelIstance = new SettingsModel();

            settingsModelIstance.setServer(new SettingsModel.Server());
            //settingsModelIstance.user = new SettingsModel.User();
            //settingsModelIstance.userSession = new SettingsModel.UserSession();

            firstStart = true;
        }

        dbSettingsModelIstance = new DBSettingsModel();

    }

    public static SettingsModel i() {
        return settingsModelIstance;
    }

    public static DBSettingsModel iDB() {
        return dbSettingsModelIstance;
    }

    public static boolean isFirstStart() {
        return firstStart;
    }

    public static boolean isInstanceAvailable() {
        return i() != null;
    }

    public static void update() {
        Stash.put(TAG, settingsModelIstance);
    }


    public static void loadDBVariables(RunnableArgs<String> onProgress, Runnable onComplete, RunnableArgs<Exception> onFailed) {
        dbSettingsModelIstance = new DBSettingsModel();

        Trace perfTrace = UtilityFirebase.getNewPerformanceTrace("db_load_vars");
        perfTrace.start();

        Runnable tmpOnComplete = () -> {
            perfTrace.stop();
            onComplete.run();
        };

        RunnableArgs<Exception> tmpOnFailed = ex -> {
            perfTrace.putAttribute("failed", "true");
            if (!(ex instanceof SocketTimeoutException)) onFailed.run(ex);
            else
                onFailed.run(new Exception("Errore durante il caricamento dei dati. Riavviare l'applicazione!"));
        };

        onProgress.run("dati azienda");
        loadDatiAzienda(() -> {

            onProgress.run("profili");
            loadAvailableProfiles(() -> {

                onProgress.run("depositi");
                loadAvailableCodMdeps(() -> {

                    onProgress.run("posizioni");
                    loadAvailablePosizioni(() -> {

                        onProgress.run("impostazioni");
                        loadGestSetupValues(tmpOnComplete, tmpOnFailed);
                    }, tmpOnFailed);
                }, tmpOnFailed);
            }, tmpOnFailed);
        }, tmpOnFailed);
    }

    private static void loadDatiAzienda(Runnable onComplete, RunnableArgs<Exception> onFailed) {

        SystemRESTConsumer.getAzienda(datiAzienda -> {
            SettingsManager.iDB().setDatiAzienda(datiAzienda);

            if (onComplete != null) onComplete.run();
        }, onFailed);
    }

    private static void loadAvailableProfiles(Runnable onComplete, RunnableArgs<Exception> onFailed) {
        SystemRESTConsumer.getAvailableProfiles(availableProfiles -> {
            dbSettingsModelIstance.setAvailableProfiles(availableProfiles);

            onComplete.run();

        }, ex -> {
            //BOH
            if (onFailed != null) onFailed.run(ex);
        });
    }

    private static void loadAvailableCodMdeps(Runnable onComplete, RunnableArgs<Exception> onFailed) {
        SystemRESTConsumer.getAvailableCodMdeps(availableCodMdeps -> {
            dbSettingsModelIstance.setAvailableCodMdep(availableCodMdeps);

            if (availableCodMdeps == null || availableCodMdeps.size() == 0) {
                onFailed.run(new Exception(mContext.getText(R.string.no_codmdep_available).toString()));
                return;
            }

            boolean codMdepExistsAnymore = false;

            if (settingsModelIstance.getUserSession().getDepo() != null) {
                for (AvailableCodMdepsDTO availableCodMdepDTO : availableCodMdeps) {
                    //Controllo se il codMdep salvato esiste ancora
                    if (availableCodMdepDTO.getCodMdep().equalsIgnoreCase(settingsModelIstance.getUserSession().getDepo().getCodMdep())) {
                        codMdepExistsAnymore = true;
                        break;
                    }
                }
            }

            if (!codMdepExistsAnymore) {
                settingsModelIstance.getUserSession().setDepo(availableCodMdeps.get(0));
            }

            if (onComplete != null) onComplete.run();
        }, ex -> {
            //BOH
            if (onFailed != null) onFailed.run(ex);
        });
    }


    private static void loadAvailablePosizioni(Runnable onComplete, RunnableArgs<Exception> onFailed) {
        PosizioniRESTConsumer.getAvailablePosizioni(availablePosizioni -> {
                    dbSettingsModelIstance.setAvailablePosizioni(availablePosizioni);

                    if (onComplete != null) onComplete.run();
                }, ex -> {
                    if (onFailed != null) onFailed.run(ex);
                }
        );
    }


    private static void loadGestSetupValues(Runnable onComplete, RunnableArgs<Exception> onFailed) {

        List<StbGestSetup> stbGestSetupList = new ArrayList<>();

        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("DEFAULT_CAUSALE_RETTIFICA_GIACENZE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("ENABLE_CHECK_PARTITA_MAG_PICKING_V"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("FLAG_MULTI_CLIENTE_ORD_VENDITA"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("COD_ANAG_DEFAULT"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("DEFAULT_CRITERIO_DISTRIBUZIONE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("PICKING_LIBERO")
                .setKeySection("FLAG_ASK_CLIENTE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("PICKING_LIBERO")
                .setKeySection("FLAG_ALLOW_EMPTY_CLIENTE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_CAN_ADD_EXTRA_ITEMS"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("ACCETTAZIONE")
                .setKeySection("FLAG_AUTO_OPEN_NEW_UL"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_CAN_ADD_EXTRA_QUANTITY"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("ENABLE_CHECK_DEPOSITO"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("FLAG_FORCE_ALL_TO_COLLI"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("FLAG_ASK_PESO_COLLO"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_USE_NEW_PICKING_LIST"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("PRODUZIONE")
                .setKeySection("FLAG_VERSAMENTO_DIRETTO"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("PRODUZIONE")
                .setKeySection("DEFAULT_COD_ANAG"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("ENABLE_MANUAL_PICK"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("ENABLE_FAKE_GIACENZA"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_CAN_SELECT_MULTIPLE_ORDERS"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("REPORT_PACKING_LIST"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_USE_QTA_ORD"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("ON_NUM_CNF_INPUT_CHANGED"));


        GestSetupRESTConsumer.getValues(stbGestSetupList, list -> {
            dbSettingsModelIstance.setDefaultCausaleRettificaGiacenze(getValueFromList(list, "SETUP", "DEFAULT_CAUSALE_RETTIFICA_GIACENZE", String.class));
            dbSettingsModelIstance.setEnableCheckPartitaMagCheckPickingV(getValueFromList(list, "SETUP", "ENABLE_CHECK_PARTITA_MAG_PICKING_V", Boolean.class));
            dbSettingsModelIstance.setFlagMultiClienteOrdV(getValueFromList(list, "SETUP", "FLAG_MULTI_CLIENTE_ORD_VENDITA", Boolean.class));
            dbSettingsModelIstance.setDefaultCodAnag(getValueFromList(list, "SETUP", "COD_ANAG_DEFAULT", String.class));
            dbSettingsModelIstance.setDefaultCriterioDistribuzione(getValueFromList(list, "SETUP", "DEFAULT_CRITERIO_DISTRIBUZIONE", String.class));
            dbSettingsModelIstance.setFlagAskClienteInPickingLibero(getValueFromList(list, "PICKING_LIBERO", "FLAG_ASK_CLIENTE", Boolean.class));
            dbSettingsModelIstance.setFlagAllowEmptyClienteInPickingLibero(getValueFromList(list, "PICKING_LIBERO", "FLAG_ALLOW_EMPTY_CLIENTE", Boolean.class));
            dbSettingsModelIstance.setFlagCanAddExtraItemSpedizione(getValueFromList(list, "SPEDIZIONE", "FLAG_CAN_ADD_EXTRA_ITEMS", Boolean.class));
            dbSettingsModelIstance.setFlagCanAutoOpenNewULAccettazione(getValueFromList(list, "ACCETTAZIONE", "FLAG_AUTO_OPEN_NEW_UL", Boolean.class));
            dbSettingsModelIstance.setFlagCanAddExtraQuantitySpedizione(getValueFromList(list, "SPEDIZIONE", "FLAG_CAN_ADD_EXTRA_QUANTITY", Boolean.class));
            dbSettingsModelIstance.setFlagEnableCheckDepositoSpedizione(getValueFromList(list, "SPEDIZIONE", "ENABLE_CHECK_DEPOSITO", Boolean.class));
            dbSettingsModelIstance.setFlagForceAllToColli(getValueFromList(list, "SETUP", "FLAG_FORCE_ALL_TO_COLLI", Boolean.class));
            dbSettingsModelIstance.setFlagAskPesoColloSpedizione(getValueFromList(list, "SETUP", "FLAG_ASK_PESO_COLLO", Boolean.class));
            dbSettingsModelIstance.setFlagUseNewPickingListSpedizione(getValueFromList(list, "SPEDIZIONE", "FLAG_USE_NEW_PICKING_LIST", Boolean.class));
            dbSettingsModelIstance.setFlagVersamentoDirettoProduzione(getValueFromList(list, "PRODUZIONE", "FLAG_VERSAMENTO_DIRETTO", Boolean.class));
            dbSettingsModelIstance.setProduzioneDefaultCodAnag(getValueFromList(list, "PRODUZIONE", "DEFAULT_COD_ANAG", String.class));
            dbSettingsModelIstance.setFlagSpedizioneEnableManualPick(getValueFromList(list, "SPEDIZIONE", "ENABLE_MANUAL_PICK", Boolean.class));
            dbSettingsModelIstance.setFlagSpedizioneEnableFakeGiacenza(getValueFromList(list, "SPEDIZIONE", "ENABLE_FAKE_GIACENZA", Boolean.class));
            dbSettingsModelIstance.setFlagSpedizioneCanSelectMultipleOrders(getValueFromList(list, "SPEDIZIONE", "FLAG_CAN_SELECT_MULTIPLE_ORDERS", Boolean.class));
            dbSettingsModelIstance.setReportNameSpedizionChiudiOrdine(getValueFromList(list, "SPEDIZIONE", "REPORT_PACKING_LIST", String.class));
            dbSettingsModelIstance.setFlagSpedizioneUseQtaOrd(getValueFromList(list, "SPEDIZIONE", "FLAG_USE_QTA_ORD", Boolean.class));

            Integer onNumCnfInputChanged = getValueFromList(list, "SETUP", "ON_NUM_CNF_INPUT_CHANGED", Integer.class);
            if (onNumCnfInputChanged != null) {
                dbSettingsModelIstance.setOnNumCnfInputChanged(onNumCnfInputChanged);
            }

            if (onComplete != null) onComplete.run();
        }, onFailed);

    }


    public static <T> T getValueFromList(List<StbGestSetup> stbGestSetupList, String section, String keySectionName, Class<T> clazz) {

        StbGestSetup value = Stream.of(stbGestSetupList)
                .filter(x -> x.getSection().equalsIgnoreCase(section) && x.getKeySection().equalsIgnoreCase(keySectionName))
                .findFirst().get();

        if (clazz == String.class) {
            return clazz.cast(value.getValue());
        } else if (clazz == Boolean.class) {
            return clazz.cast("S".equalsIgnoreCase(value.getValue()));
        } else if (clazz == Integer.class && value.getValue() != null) {
            return clazz.cast(Integer.parseInt(value.getValue()));
        } else return null;

    }

}
