package it.integry.integrywmsnative.core.settings;

import android.content.Context;

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

import java.lang.reflect.Type;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
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.model.dto.InternalCodAnagsDTO;
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("depositi");
        loadAvailableCodMdeps(() -> {

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

                onProgress.run("impostazioni");
                loadGestSetupValues(() -> {

                    onProgress.run("dati azienda");
                    loadDatiAzienda(tmpOnComplete, tmpOnFailed);
                }, tmpOnFailed);
            }, tmpOnFailed);
        }, tmpOnFailed);
    }

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

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

            if (!SettingsManager.iDB().isFlagUseCodAnagAziendale()) {
                onComplete.run();
                return;
            }

            String internalCodAnagsQuery = "SELECT ga.cod_anag,\n" +
                    "       CAST(CASE WHEN vc.cod_anag IS NOT NULL THEN 1 ELSE 0 END AS BIT) AS is_cliente,\n" +
                    "       CAST(CASE WHEN af.cod_anag IS NOT NULL THEN 1 ELSE 0 END AS BIT) AS is_fornitore\n" +
                    "FROM azienda\n" +
                    "INNER JOIN gtb_anag ga on azienda.part_iva = ga.part_iva\n" +
                    "LEFT OUTER JOIN vtb_clie vc on ga.cod_anag = vc.cod_anag AND vc.flag_stato = 'A'\n" +
                    "LEFT OUTER JOIN atb_forn af on ga.cod_anag = af.cod_anag AND af.flag_stato = 'A'";

            Type type = new TypeToken<List<InternalCodAnagsDTO>>() {
            }.getType();
            SystemRESTConsumer.<List<InternalCodAnagsDTO>>processSqlStatic(internalCodAnagsQuery, type, internalCodAnagsList -> {
                SettingsManager.iDB().setInternalCodAnags(internalCodAnagsList);

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

        }, 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.getAvailableCodMdepsStatic(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("FLAG_USE_COD_ANAG_AZIENDALE"));
        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("FILTER_FORNTIORE_PROD"));
        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("FLAG_USE_QTA_ORD"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("ON_NUM_CNF_INPUT_CHANGED"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SETUP")
                .setKeySection("ON_QTA_TOT_INPUT_CHANGED"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PVM")
                .setSection("DOC_INTERNI")
                .setKeySection("NOTE_PERDITA"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PVM")
                .setSection("ORDINI_A")
                .setKeySection("ORDINA_NUOVI_ARTICOLI"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PVM")
                .setSection("ORDINI_A")
                .setKeySection("ORDINA_ARTICOLI_ON_SCAN"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PVM")
                .setSection("DOC_INTERNI")
                .setKeySection("CHECK_FORNITORE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("ACCETTAZIONE")
                .setKeySection("FLAG_USE_QTA_ORD"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("PICKING_LIBERO")
                .setKeySection("FLAG_ALLOW_EMPTY_DEST"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_PRINT_PACKING_LIST_ON_CLOSE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_PRINT_ETICHETTE_ON_CLOSE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_ALLOW_EMPTY_DEST"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_ASK_INFO_AGGIUNTIVE"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("COD_DTIP_ORD_TRASF"));
        stbGestSetupList.add(new StbGestSetup()
                .setGestName("PICKING")
                .setSection("SPEDIZIONE")
                .setKeySection("FLAG_NOTIFICA_STATO_PARTITA"));
        String codMdep = SettingsManager.i().getUserSession().getDepo().getCodMdep();

        GestSetupRESTConsumer.getValues(codMdep, 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.setFlagUseCodAnagAziendale(getValueFromList(list, "SETUP", "FLAG_USE_COD_ANAG_AZIENDALE", Boolean.class));
            dbSettingsModelIstance.setDefaultCriterioDistribuzione(getValueFromList(list, "SETUP", "DEFAULT_CRITERIO_DISTRIBUZIONE", String.class));
            dbSettingsModelIstance.setFlagAskClienteInPickingLibero(getValueFromList(list, "PICKING_LIBERO", "FLAG_ASK_CLIENTE", Boolean.class));
            dbSettingsModelIstance.setFlagPickLiberoAllowEmptyCliente(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.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.setFlagSpedizioneUseQtaOrd(getValueFromList(list, "SPEDIZIONE", "FLAG_USE_QTA_ORD", Boolean.class));
            dbSettingsModelIstance.setFlagOrdinaNuoviArticoliInGriglia(getValueFromList(list, "ORDINI_A", "ORDINA_NUOVI_ARTICOLI", Boolean.class));
            dbSettingsModelIstance.setDocInterniCheckFornitore(getValueFromList(list, "DOC_INTERNI", "CHECK_FORNITORE", String.class));
            dbSettingsModelIstance.setFlagAccettazioneUseQtaOrd(getValueFromList(list, "ACCETTAZIONE", "FLAG_USE_QTA_ORD", Boolean.class));
            dbSettingsModelIstance.setFlagPickLiberoAllowEmptyDest(getValueFromList(list, "PICKING_LIBERO", "FLAG_ALLOW_EMPTY_DEST", Boolean.class));
            dbSettingsModelIstance.setFlagOrdinaArticoliOnScan(getValueFromList(list, "ORDINI_A", "ORDINA_ARTICOLI_ON_SCAN", Boolean.class));
            dbSettingsModelIstance.setFlagPrintPackingListOnOrderClose(getValueFromList(list, "SPEDIZIONE", "FLAG_PRINT_PACKING_LIST_ON_CLOSE", Boolean.class));
            dbSettingsModelIstance.setFlagPrintEtichetteOnOrderClose(getValueFromList(list, "SPEDIZIONE", "FLAG_PRINT_ETICHETTE_ON_CLOSE", Boolean.class));
            dbSettingsModelIstance.setFlagAskInfoAggiuntiveSpedizione(getValueFromList(list, "SPEDIZIONE", "FLAG_ASK_INFO_AGGIUNTIVE", Boolean.class));
            dbSettingsModelIstance.setFilterFornitoreProd(getValueFromList(list, "PRODUZIONE", "FILTER_FORNTIORE_PROD", String.class));
            dbSettingsModelIstance.setCodDtipOrdTrasfV(getValueFromList(list, "SPEDIZIONE", "COD_DTIP_ORD_TRASF", String.class));
            dbSettingsModelIstance.setNotifyLotStatus(getValueFromList(list, "SPEDIZIONE", "FLAG_NOTIFICA_STATO_PARTITA", Boolean.class));

            String notePerdita = getValueFromList(list, "DOC_INTERNI", "NOTE_PERDITA", String.class);
            if (notePerdita != null)
                dbSettingsModelIstance.setNotePerditaDocInterni(Arrays.asList(notePerdita.split("\\|")));

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

            Integer onQtaTotInputChanged = getValueFromList(list, "SETUP", "ON_QTA_TOT_INPUT_CHANGED", Integer.class);
            if (onQtaTotInputChanged != null)
                dbSettingsModelIstance.setOnQtaTotInputChanged(onQtaTotInputChanged);

            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))
                .findFirstOrElse(new StbGestSetup());

        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;

    }

}
