package com.dreamauthlogin;

import android.util.Log;
import android.content.pm.PackageManager;

import androidx.annotation.NonNull;

import com.dream.sports.consumer.AuthConsumerConfig;
import com.dream.sports.consumer.AuthConsumerPlugin;
import com.dream.sports.consumer.IAuthLoginCallback;
import com.dream.sports.consumer.constants.AccessScopes;
import com.dream.sports.consumer.constants.AuthPacketResult;
import com.dream.sports.consumer.constants.CodeChallengeMethod;
import com.dream.sports.consumer.constants.DAuthStatusCodes;
import com.dream.sports.consumer.constants.DreamAuthProviderHost;
import com.dream.sports.consumer.constants.Prompt;
import com.dream.sports.consumer.constants.ResponseType;
import com.dream.sports.pluggermodule.base.IPluginConfig;
import com.dream.sports.pluggermodule.config.Delay;
import com.dream.sports.pluggermodule.config.HttpConfig;
import com.dream.sports.pluggermodule.config.RetrialConfig;
import com.dream.sports.pluggermodule.config.RetryPolicy;
import com.dream.sports.pluggermodule.config.TimeoutConfig;
import com.dream.sports.pluggermodule.playground.PluginConfig;
import com.dream.sports.pluggermodule.playground.Plugins;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.google.gson.Gson;
import com.pluggerreactnative.PluggerReactNativeModule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import kotlin.reflect.KFunction;


@ReactModule(name = LoginWithDAuthModule.MODULE_NAME)
public class LoginWithDAuthModule extends ReactContextBaseJavaModule implements IAuthLoginCallback {

  public static final String MODULE_NAME = "RNDreamAuthLogin";
  public static final String DEFAULT_SCOPE = "openid";
  private static Promise signInPromise;
  private static Gson gson = new Gson();

  @Override
  public String getName() {
    return MODULE_NAME;
  }

  public LoginWithDAuthModule(final ReactApplicationContext reactContext) {
    super(reactContext);
  }

  @Override
  public Map<String, Object> getConstants() {
    final Map<String, Object> constants = new HashMap<>();
    constants.put("LOGGED_IN", String.valueOf(DAuthStatusCodes.LOGGED_IN));
    constants.put("LOGIN_CANCELLED", String.valueOf(DAuthStatusCodes.LOGIN_CANCELLED));
    constants.put("CONSENT_DENIED", String.valueOf(DAuthStatusCodes.CONSENT_DENIED));
    constants.put("SOMETHING_WENT_WRONG", String.valueOf(DAuthStatusCodes.SOMETHING_WENT_WRONG));
    return constants;
  }

  @ReactMethod
  public void configure(final ReadableMap config, final Promise promise) {
    final ReadableArray scopes = config.hasKey("scopes") ? config.getArray("scopes") : Arguments.createArray();
    final String clientName = config.hasKey("clientName") ? config.getString("clientName") : null;
    final String clientId = config.hasKey("clientId") ? config.getString("clientId") : null;
    promise.resolve(null);
  }

  @ReactMethod
  public Promise signIn(final Promise promise) {
    signInPromise = promise;
    try {
      Objects.requireNonNull(PluggerReactNativeModule.<AuthConsumerPlugin>getPlugin(Plugins.AUTH_CONSUMER)).getAuthConsumerService().signIn();
    } catch (Exception e) {
      e.printStackTrace();
      signInPromise.reject(e.getMessage(), e);
    }
    return signInPromise;
  }


  private static HttpConfig buildHttpConfig(HashMap<String, Object> rnHttpConfig) {
    HttpConfig httpConfig = null;
    try {
      String apiBaseUrl = "";
      boolean shouldRetry = true;
      long fetchInterval = 200L;
      int attempts = 0;
      long retrialDelay = 500L;
      RetryPolicy retrialPolicy = RetryPolicy.LINEAR;
      long callTimeout = 5000L;
      apiBaseUrl = (String) rnHttpConfig.get("apiBaseUrl");
      shouldRetry = (Boolean) rnHttpConfig.get("shouldRetry");
      HashMap<String, String> headers = (HashMap<String, String>) (rnHttpConfig.containsKey("headers") ? rnHttpConfig.get("headers") : new HashMap<String, String>());

      HashMap<String, Object> retrialConfig = (HashMap<String, Object>) (rnHttpConfig.containsKey("retrialConfig") ? rnHttpConfig.get("retrialConfig") : null);
      HashMap<String, Object> timeoutConfig = (HashMap<String, Object>) (rnHttpConfig.containsKey("timeoutConfig") ? rnHttpConfig.get("timeoutConfig") : null);
      if (retrialConfig != null) {
        attempts = (int) (retrialConfig.containsKey("attempts") ? ((Double) retrialConfig.get("attempts")).intValue() : 1);
        HashMap<String, Object> delay = (HashMap<String, Object>) (retrialConfig.containsKey("delay") ? retrialConfig.get("delay") : null);
        if (delay != null) {
          retrialDelay = ((Number) Objects.requireNonNull(delay.get("time"))).longValue();
          String policy = (String) delay.get("policy");
          if (Objects.equals(policy, RetryPolicy.INCREMENTAL.toString())) {
            retrialPolicy = RetryPolicy.INCREMENTAL;
          } else if (Objects.equals(policy, RetryPolicy.QUADRATIC.toString())) {
            retrialPolicy = RetryPolicy.QUADRATIC;
          }
        }
      }

      if (timeoutConfig != null) {
        callTimeout = ((Number) Objects.requireNonNull(timeoutConfig.get("callTimeout"))).longValue();
      }

      RetrialConfig retryConfig = new RetrialConfig(attempts, new Delay(retrialDelay, retrialPolicy), new HashSet<>());
      TimeoutConfig timeOutConfig = new TimeoutConfig(callTimeout, true);
      httpConfig = new HttpConfig(Objects.requireNonNull(headers), Objects.requireNonNull(apiBaseUrl), shouldRetry, fetchInterval, timeOutConfig, retryConfig);
      return httpConfig;
    } catch (Exception e) {
      Log.d("PluggerDACRN", e.getMessage());
      return httpConfig;
    }
  }

  @ReactMethod
  public Promise getDreamAuthPlugin(final Promise promise) {
    try {
      HttpConfig httpConfig = null;
      HashMap<String, Object> plugConfig = new HashMap<>();
      AuthConsumerPlugin cp = PluggerReactNativeModule.getPlugin(Plugins.AUTH_CONSUMER);
      if (cp != null) {
        promise.resolve(null);
        return promise;
      }
      Object consumerPlugin = PluggerReactNativeModule.getPluginConfig(Plugins.AUTH_CONSUMER);
      if (consumerPlugin != null) {
        plugConfig = (HashMap<String, Object>) consumerPlugin;
      }

      String clientId = "";
      String clientName = "";
      ArrayList<String> accessScopes = new ArrayList<String>();
      List<DreamAuthProviderHost> host = new ArrayList<>();

      if (plugConfig.containsKey("clientId") && plugConfig.get("clientId") instanceof String) {
        clientId = (String) plugConfig.get("clientId");
      }

      if (plugConfig.containsKey("clientName") && plugConfig.get("clientName") instanceof String) {
        clientName = (String) plugConfig.get("clientName");
      }

      if (plugConfig.containsKey("host") && plugConfig.get("host") instanceof ArrayList) {
        ArrayList<?> providedHostList = (ArrayList<?>) plugConfig.get("host");
        for (Object providedHost : providedHostList) {
          try {
            DreamAuthProviderHost providerHost = DreamAuthProviderHost.valueOf((String)providedHost);
            host.add(providerHost);
          }catch (IllegalArgumentException e){}
        }
      }

      if (plugConfig.containsKey("scopes")) {
        accessScopes = (ArrayList<String>) plugConfig.get("scopes");
      } else {
        accessScopes.add(0, DEFAULT_SCOPE);
      }

      HashMap<String, Object> rnHttpConfig = (HashMap<String, Object>) (plugConfig.containsKey("httpConfig") ? plugConfig.get("httpConfig") : new HashMap<String, Object>());
      if (rnHttpConfig == null) {
        httpConfig = PluggerReactNativeModule.sharedHttpConfig;
      } else {
        httpConfig = buildHttpConfig(rnHttpConfig);
      }
      String redirectUri = (String) plugConfig.get("redirectUri");

      assert redirectUri != null;
      assert clientId != null;
      assert clientName != null;


      Boolean state = true;
      if (plugConfig.get("state") != null) {
        state = (Boolean) plugConfig.get("state");
      }

      Boolean nonce = true;
      if (plugConfig.get("nonce") != null) {
        nonce = (Boolean) plugConfig.get("nonce");
      }

      ResponseType responseType = ResponseType.TOKEN.getString().equals(plugConfig.get("responseType")) ? ResponseType.TOKEN : ResponseType.CODE;
      Prompt prompt = Prompt.NONE.getString().equals(plugConfig.get("prompt")) ? Prompt.NONE : Prompt.LOGIN;

      CodeChallengeMethod codeChellengeMethod = CodeChallengeMethod.NONE;

      if (CodeChallengeMethod.PLAIN.getString().equals(plugConfig.get("codeChellengeMethod"))) {
        codeChellengeMethod = CodeChallengeMethod.PLAIN;
      }

      if (CodeChallengeMethod.S256.getString().equals(plugConfig.get("codeChellengeMethod"))) {
        codeChellengeMethod = CodeChallengeMethod.S256;
      }


      AuthConsumerConfig consumerConfig = new AuthConsumerConfig.Builder(clientId, clientName, host, accessScopes, this, redirectUri)
        .setState(state)
        .setNonce(nonce)
        .setPrompt(prompt)
        .setCodeChallengeMethod(codeChellengeMethod)
        .setResponseType(responseType)
        .setHttpConfig(httpConfig)
        .build();

      PluginConfig pluginConfig = new PluginConfig((KFunction<?>) DreamAuthPlugin.Companion.getPlugin(), Plugins.AUTH_CONSUMER.getPluginName(), consumerConfig);
      PluggerReactNativeModule.addPlugin(pluginConfig);
      Log.d("PluggerDACRN", "getDreamAuthPlugin: Plugin added");
      PluggerReactNativeModule.<AuthConsumerPlugin>getPlugin(Plugins.AUTH_CONSUMER);
      Log.d("PluggerDACRN", "getDreamAuthPlugin:success");
      promise.resolve(null);
    } catch (Exception e) {
      Log.e("PluggerDACRN", "error:getDreamAuthPlugin: " + e);
      promise.reject(e.getMessage(), e);
    }
    return promise;
  }

  @Override
  public void onSuccess(@NonNull AuthPacketResult authPacketResult) {
    String response = gson.toJson(authPacketResult);
    Log.d("PluggerDACRN", "onSuccess " + response);
    signInPromise.resolve(response);
  }

  @Override
  public void onFailure(@NonNull DAuthStatusCodes dAuthStatusCodes, @NonNull String s) {
    Log.d("PluggerDACRN", "onFailure " + dAuthStatusCodes.getString() + " " + s);
    signInPromise.reject(dAuthStatusCodes.getString(), s);
  }

  @ReactMethod(isBlockingSynchronousMethod = true)
  public boolean install() {
    try {
      System.loadLibrary("plugDreamAuthcpp");

      ReactApplicationContext context = getReactApplicationContext();
      nativeInstall(context.getJavaScriptContextHolder().get());
      return true;
    } catch (Exception exception) {
      return false;
    }
  }

  public Boolean getDreamAuthPluginSync() {
    try {
      HttpConfig httpConfig = null;
      HashMap<String, Object> plugConfig = new HashMap<>();
      AuthConsumerPlugin cp = PluggerReactNativeModule.getPlugin(Plugins.AUTH_CONSUMER);
      if (cp != null) {
        return true;
      }
      Object consumerPlugin = PluggerReactNativeModule.getPluginConfig(Plugins.AUTH_CONSUMER);
      if (consumerPlugin != null) {
        plugConfig = (HashMap<String, Object>) consumerPlugin;
      }

      String clientId = "";
      String clientName = "";
      ArrayList<String> accessScopes = new ArrayList<String>();
      List<DreamAuthProviderHost> host = new ArrayList<>();

      if (plugConfig.containsKey("clientId") && plugConfig.get("clientId") instanceof String) {
        clientId = (String) plugConfig.get("clientId");
      }

      if (plugConfig.containsKey("clientName") && plugConfig.get("clientName") instanceof String) {
        clientName = (String) plugConfig.get("clientName");
      }

      if (plugConfig.containsKey("host") && plugConfig.get("host") instanceof ArrayList) {
        ArrayList<?> providedHostList = (ArrayList<?>) plugConfig.get("host");
        for (Object providedHost : providedHostList) {
          try {
            DreamAuthProviderHost providerHost = DreamAuthProviderHost.valueOf((String)providedHost);
            host.add(providerHost);
          }catch (IllegalArgumentException e){}
        }
      }

      if (plugConfig.containsKey("scopes")) {
        accessScopes = (ArrayList<String>) plugConfig.get("scopes");
      } else {
        accessScopes.add(0, DEFAULT_SCOPE);
      }

      HashMap<String, Object> rnHttpConfig = (HashMap<String, Object>) (plugConfig.containsKey("httpConfig") ? plugConfig.get("httpConfig") : new HashMap<String, Object>());
      if (rnHttpConfig == null) {
        httpConfig = PluggerReactNativeModule.sharedHttpConfig;
      } else {
        httpConfig = buildHttpConfig(rnHttpConfig);
      }
      String redirectUri = (String) plugConfig.get("redirectUri");

      assert redirectUri != null;
      assert clientId != null;
      assert clientName != null;


      Boolean state = true;
      if (plugConfig.get("state") != null) {
        state = (Boolean) plugConfig.get("state");
      }

      Boolean nonce = true;
      if (plugConfig.get("nonce") != null) {
        nonce = (Boolean) plugConfig.get("nonce");
      }

      ResponseType responseType = ResponseType.TOKEN.getString().equals(plugConfig.get("responseType")) ? ResponseType.TOKEN : ResponseType.CODE;
      Prompt prompt = Prompt.NONE.getString().equals(plugConfig.get("prompt")) ? Prompt.NONE : Prompt.LOGIN;

      CodeChallengeMethod codeChellengeMethod = CodeChallengeMethod.NONE;

      if (CodeChallengeMethod.PLAIN.getString().equals(plugConfig.get("codeChellengeMethod"))) {
        codeChellengeMethod = CodeChallengeMethod.PLAIN;
      }

      if (CodeChallengeMethod.S256.getString().equals(plugConfig.get("codeChellengeMethod"))) {
        codeChellengeMethod = CodeChallengeMethod.S256;
      }

      AuthConsumerConfig consumerConfig = new AuthConsumerConfig.Builder(clientId, clientName, host, accessScopes, this, redirectUri)
        .setState(state)
        .setNonce(nonce)
        .setPrompt(prompt)
        .setCodeChallengeMethod(codeChellengeMethod)
        .setResponseType(responseType)
        .setHttpConfig(httpConfig)
        .build();

      PluginConfig pluginConfig = new PluginConfig((KFunction<?>) DreamAuthPlugin.Companion.getPlugin(), Plugins.AUTH_CONSUMER.getPluginName(), consumerConfig);
      PluggerReactNativeModule.addPlugin(pluginConfig);
      Log.d("PluggerDACRN", "getDreamAuthPlugin: Plugin added");
      if (PluggerReactNativeModule.<AuthConsumerPlugin>getPlugin(Plugins.AUTH_CONSUMER) == null) {
        return false;
      }
      Log.d("PluggerDACRN", "getDreamAuthPlugin:success");
      return true;
    } catch (Exception e) {
      Log.e("PluggerDACRN", "error:getDreamAuthPlugin: " + e);
      return false;
    }
  }

  public static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
    try {
      return packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA).enabled;
    } catch (PackageManager.NameNotFoundException e) {
      return false;
    }
  }
  
  public static final Map<DreamAuthProviderHost, String> DreamAuthProviderHostPackage = new HashMap<>();
  static {
        DreamAuthProviderHostPackage.put(DreamAuthProviderHost.Dream11, "com.app.dream11Pro");
        DreamAuthProviderHostPackage.put(DreamAuthProviderHost.Dream11Stag, "com.app.dream11staging");
        DreamAuthProviderHostPackage.put(DreamAuthProviderHost.Dream11PlayStore, "com.dream11.fantasy.cricket.football.kabaddi");
  }

  private native void nativeInstall(long jsiPtr);

}
