package it.integry.integrywmsnative.core.rest.consumers;

import android.text.TextUtils;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.orhanobut.logger.Logger;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.inject.Singleton;

import it.integry.integrywmsnative.BuildConfig;
import it.integry.integrywmsnative.core.CommonConst;
import it.integry.integrywmsnative.core.exception.InvalidConnectionException;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.model.Azienda;
import it.integry.integrywmsnative.core.rest.RESTBuilder;
import it.integry.integrywmsnative.core.rest.model.AvailableCodMdepsDTO;
import it.integry.integrywmsnative.core.rest.model.MailRequestDTO;
import it.integry.integrywmsnative.core.rest.model.NativeSqlRequestDTO;
import it.integry.integrywmsnative.core.rest.model.ServiceRESTResponse;
import it.integry.integrywmsnative.core.settings.SettingsManager;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityLogger;
import it.integry.integrywmsnative.core.utility.UtilityString;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

@Singleton
public class SystemRESTConsumer extends _BaseRESTConsumer {

    public static <T> void processSqlStatic(String nativeSql, final Type clazz, RunnableArgs<T> onComplete, RunnableArgs<Exception> onFailed) {

        NativeSqlRequestDTO nativeSqlDTO = new NativeSqlRequestDTO();
        nativeSqlDTO.nativeSql = nativeSql;

        SystemRESTConsumerService service = RESTBuilder.getService(SystemRESTConsumerService.class);
        service
                .processSql(nativeSqlDTO)
                .enqueue(new Callback<>() {
                    @Override
                    public void onResponse(Call<ServiceRESTResponse<Object>> call, Response<ServiceRESTResponse<Object>> response) {
                        analyzeAnswer(response, "ProcessSql", o -> {
                            Gson gson = new GsonBuilder()
                                    .registerTypeAdapter(Date.class, (JsonDeserializer<?>) (json, typeOfT, context) -> {
                                        try {
                                            return UtilityDate.recognizeDate(json.getAsString());
                                        } catch (Exception e) {
                                            throw new JsonParseException(e);
                                        }
                                    })
                                    .create();


                            String json = gson.toJson(o);

                            InputStream ims = new ByteArrayInputStream(json.getBytes());
                            Reader reader = new InputStreamReader(ims);
                            T gsonObj = gson.fromJson(reader, clazz);

                            onComplete.run(gsonObj);
                        }, onFailed);
                    }

                    @Override
                    public void onFailure(Call<ServiceRESTResponse<Object>> call, Throwable t) {
                        Logger.e(t, "ProcessSQL");

                        if (t instanceof ConnectException)
                            onFailed.run(new InvalidConnectionException(RESTBuilder.getDefaultHost(), RESTBuilder.getDefaultPort(), t));
                        else
                            onFailed.run(new Exception(t));
                    }
                });

    }

    public static <T> void processSqlStatic(String nativeSql, final Type clazz, final ISimpleOperationCallback<T> callback) {

        processSqlStatic(nativeSql, clazz, data -> {
            callback.onSuccess((T) data);
        }, callback::onFailed);

    }

    public <T> void processSql(String nativeSql, final Type clazz, final RunnableArgs<T> onComplete, final RunnableArgs<Exception> onFailed) {
        processSqlStatic(nativeSql, clazz, onComplete, onFailed);
    }


    public static void getAvailableProfiles(final RunnableArgs<List<String>> onSuccess, RunnableArgs<Exception> onFailed) {

        SystemRESTConsumerService service = RESTBuilder.getService(SystemRESTConsumerService.class);
        service.getAvailableProfiles(SettingsManager.i().getUser().getUsername(), SettingsManager.i().getUser().getPassword()).enqueue(new Callback<ServiceRESTResponse<List<String>>>() {
            @Override
            public void onResponse(Call<ServiceRESTResponse<List<String>>> call, Response<ServiceRESTResponse<List<String>>> response) {
                analyzeAnswer(response, "ProfilesAvailable", onSuccess, onFailed);
            }

            @Override
            public void onFailure(Call<ServiceRESTResponse<List<String>>> call, final Throwable t) {
                Log.e("ProfilesAvailable", t.toString());
                onFailed.run(new Exception(t));
                UtilityLogger.errorMe(new Exception(t));
            }
        });


    }


    public void getAvailableCodMdeps(final RunnableArgs<List<AvailableCodMdepsDTO>> onSuccess, RunnableArgs<Exception> onFailed) {
        getAvailableCodMdepsStatic(onSuccess, onFailed);
    }

    public static void getAvailableCodMdepsStatic(final RunnableArgs<List<AvailableCodMdepsDTO>> onSuccess, RunnableArgs<Exception> onFailed) {

        SystemRESTConsumerService service = RESTBuilder.getService(SystemRESTConsumerService.class);
        service.getAvailableCodMdeps().enqueue(new Callback<>() {
            @Override
            public void onResponse(Call<ServiceRESTResponse<List<AvailableCodMdepsDTO>>> call, Response<ServiceRESTResponse<List<AvailableCodMdepsDTO>>> response) {
                analyzeAnswer(response, "CodMdepsAvailable", onSuccess, onFailed);
            }

            @Override
            public void onFailure(Call<ServiceRESTResponse<List<AvailableCodMdepsDTO>>> call, final Throwable t) {
                Log.e("CodMdepsAvailable", t.toString());
                onFailed.run(new Exception(t));
            }
        });
    }


    public static void sendErrorLogMail(String message) {
        sendErrorLogMail(message, null, null);
    }


    public static void sendErrorLogMail(String message, Runnable onComplete, RunnableArgs<Exception> onFailed) {

        String currentAzienda = UtilityString.isNullOrEmpty(SettingsManager.i().getUserSession().getProfileDB()) ? "" : " [" + SettingsManager.i().getUserSession().getProfileDB() + "]";

        String dest = "";
        if (BuildConfig.DEBUG) {
            dest = TextUtils.join(";", CommonConst.Mail.forErrorsDebug);
        } else {
            dest = TextUtils.join(";", CommonConst.Mail.forErrors);
        }


        MailRequestDTO mailDTO = new MailRequestDTO()
                .setFrom("sender@integry.it")
                .setFromName((BuildConfig.DEBUG ? "[DEBUG] " : "") + "WMS Android")
                .setTo(dest)
                .setSubject("Bug notification" + currentAzienda)
                .setMsgText(message)
                .setHtml(true);

        sendMail(mailDTO, () -> {
            if (onComplete != null) onComplete.run();
        }, ex -> {
            Log.e(SystemRESTConsumer.class.getName(), "", ex);
            if (onFailed != null) onFailed.run(ex);
        });
    }


    public static void sendMail(MailRequestDTO mailDTO, Runnable onComplete, RunnableArgs<Exception> onFailed) {

        SystemRESTConsumerService service = RESTBuilder.getService(SystemRESTConsumerService.class);
        service.sendMail(mailDTO).enqueue(new Callback<ServiceRESTResponse<String>>() {
            @Override
            public void onResponse(Call<ServiceRESTResponse<String>> call, Response<ServiceRESTResponse<String>> response) {
                if (onComplete != null) onComplete.run();
            }

            @Override
            public void onFailure(Call<ServiceRESTResponse<String>> call, Throwable t) {
                if (onFailed != null) {
                    onFailed.run(new Exception(t));
                }
            }
        });
    }


    public static void getAzienda(RunnableArgs<Azienda> onComplete, RunnableArgs<Exception> onFailed) {

        String sql = "SELECT TOP 1 * FROM azienda";

        Type typeOfObjectsList = new TypeToken<ArrayList<Azienda>>() {
        }.getType();
        SystemRESTConsumer.processSqlStatic(sql, typeOfObjectsList, new ISimpleOperationCallback<List<Azienda>>() {
            @Override
            public void onSuccess(List<Azienda> value) {
                if (onComplete != null) onComplete.run(value.get(0));
            }

            @Override
            public void onFailed(Exception ex) {
                if (onFailed != null) onFailed.run(ex);
            }
        });

    }
}
