package com.reactnativerangersapplogreactnativeplugin;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.facebook.react.ReactRootView;
import com.facebook.react.bridge.ReactApplicationContext;

import org.json.JSONObject;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class RangersAppLogPicker {
  private static RangersAppLogPicker instance;
  private static final String TAG = "RangersAppLogPicker";

  public static RangersAppLogPicker getInstance() {
    if (instance == null) {
      instance = new RangersAppLogPicker();
      instance.loadCallbackClass();
    }
    return instance;
  }

  private Map<String, RangersAppLogPickerEvent> listenerMap = new HashMap();
  private Integer callCount = 0;
  private Class callbackClass;

  private void loadCallbackClass() {
    try {
      callbackClass = Class.forName("com.bytedance.applog.IPickerCallback");
    } catch (Exception e) {
      Log.w(TAG, "load com.bytedance.applog.IPickerCallback failed", e);
    }
  }

  public void loadPageInfoFromJS(ReactRootView reactRootView, Object callback, Long timeout) {
    if (callback == null) {
      return;
    }
    if (reactRootView == null) {
      callFailed(callback, "failed: rootView should not be empty!");
      return;
    }

    IReactRootView rootView = new ReactRootViewDecorator(reactRootView);
    rootView.findRootTag(rootTag -> {
      RangersAppLogPickerEvent event = new RangersAppLogPickerEvent();
      event.setEventID(String.format("%d_%d", rootTag, callCount++));
      event.setRootTag(rootTag);
      event.setCallback(callback);
      event.setTimeout(timeout);
      event.setStartTime(new Date().getTime());
      listenerMap.put(event.getEventID(), event);

      new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
        @Override
        public void run() {
          if (event != null && callback != null && listenerMap.containsKey(event.getEventID())) {
            callFailed(callback, "failed: timeout!");
          }
        }
      }, timeout);

      rootView.findReactContext(context -> {
        ReactApplicationContext reactContext = (ReactApplicationContext) context;
        RangersAppLogEmitter emitter = reactContext.getNativeModule(RangersAppLogEmitter.class);
        HashMap params = new HashMap();
        params.put("eventid", event.getEventID());
        int[] location = new int[2];
        reactRootView.getLocationOnScreen(location);
        params.put("x", location[0]);
        params.put("y", location[1]);
        params.put("width", reactRootView.getWidth());
        params.put("height", reactRootView.getHeight());
        emitter.sendPagePickerEvent(rootView, params);
      });
    });
  }

  public void updatePickerInfo(String eventID, JSONObject info) {
    if (eventID == null) {
      return;
    }

    RangersAppLogPickerEvent event = listenerMap.get(eventID);
    if (event == null) {
      return;
    }

    Long nowTime = new Date().getTime();
    if (nowTime - event.getStartTime() > event.getTimeout()) {
      return;
    }

    if (event.getCallback() == null) {
      return;
    }

    if (info == null) {
      return;
    }

    callSuccess(event.getCallback(), info);
    listenerMap.remove(eventID);
  }

  private void callSuccess(Object callback, JSONObject info) {
    if (callbackClass == null) {
      return;
    }
    if (!callbackClass.isInstance(callback)) {
      return;
    }
    try {
      Method successMethod = callbackClass.getMethod("success", JSONObject.class);
      successMethod.invoke(callback, info);
    } catch(Exception e) {
      Log.w(TAG, "call callback.success failed: " + e.getMessage(), e);
    }
  }
  private void callFailed(Object callback, String message) {
    if (callbackClass == null) {
      return;
    }
    if (!callbackClass.isInstance(callback)) {
      return;
    }
    try {
      Method failedMethod = callbackClass.getMethod("failed", String.class);
      failedMethod.invoke(callback, message);
    } catch(Exception e) {
      Log.w(TAG, "call callback.failed failed: " + e.getMessage(), e);
    }
  }
}
