package com.cashfree.capacitor;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.cashfree.pg.CFPaymentService;
import com.getcapacitor.JSObject;
import com.getcapacitor.NativePlugin;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@NativePlugin(requestCodes = CFPaymentService.REQ_CODE)
public class CFBridge extends Plugin {

    private static final String TAG = "CFBridgePlugin";
    private static String callbackID;
    private final CFPaymentService cfPaymentService;

    public CFBridge() {
        cfPaymentService = CFPaymentService.getCFPaymentServiceInstance();
    }

    @PluginMethod
    public void startPaymentWEB(PluginCall call) {
        Log.d(TAG, call.getCallbackId());
        HashMap<String, String> params = jsObjectToMap(call.getData());

        if (!validate(params, call)) {
            return;
        }

        String token = getToken(params);
        String stage = getStage(params);
        String color1 = params.containsKey("color1") ? params.get("color1") : "#784BD2";
        params.remove("color1");
        String color2 = params.containsKey("color2") ? params.get("color2") : "#FFFFFF";
        params.remove("color2");
        boolean hideOrderId = params.containsKey("hideOrderId") && Boolean.parseBoolean(params.get("hideOrderId"));
        Activity activity = getActivity();
        addSource(params);
        cfPaymentService.doPayment(activity, params, token, stage, color1, color2, hideOrderId);
        Log.d(TAG, call.getCallbackId());
        callbackID = call.getCallbackId();
        call.setKeepAlive(true);
        bridge.saveCall(call);
    }

    @PluginMethod
    public void startPaymentUPI(PluginCall call) {
        Log.d(TAG, call.getCallbackId());
        HashMap<String, String> params = jsObjectToMap(call.getData());

        if (!validate(params, call)) {
            return;
        }

        String token = getToken(params);
        String stage = getStage(params);
        Activity activity = getActivity();
        addSource(params);
        cfPaymentService.upiPayment(activity, params, token, stage);
        Log.d(TAG, call.getCallbackId());
        callbackID = call.getCallbackId();
        call.setKeepAlive(true);
        bridge.saveCall(call);
    }

    private void addSource(HashMap<String, String> params) {
        params.put("source", "capacitor-android");
    }

    @PluginMethod
    public void getUPIApps(PluginCall call) {
        Log.d(TAG, call.getCallbackId());
        cfPaymentService.getUpiClients(getActivity(), upiAppList -> {
            call.resolve(getUPIJsoFromArray(upiAppList));
        });
    }

    private JSObject getUPIJsoFromArray(ArrayList<HashMap<String, String>> upiAppList) {
        JSObject jsObject = new JSObject();
        try {
            jsObject.put("apps", new JSONArray(upiAppList));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsObject;
    }

    private boolean validate(Map<String, String> params, PluginCall call) {
        if (!params.containsKey("tokenData")) {
            call.reject("\"tokenData\" is not present in the params.");
            return false;
        }
        if (!params.containsKey("stage")) {
            call.reject("\"stage\" is not present in the params.");
            return false;
        }
        return true;
    }

    private String getToken(Map<String, String> paramsMap) {
        return paramsMap.get("tokenData");
    }

    private String getStage(Map<String, String> paramsMap) {
        return paramsMap.get("stage");
    }

    private static HashMap<String, String> jsObjectToMap(JSObject json) {
        HashMap<String, String> retMap = new HashMap<>();

        if (json != JSObject.NULL) {
            retMap = toMap(json);
        }
        return retMap;
    }

    private static HashMap<String, String> toMap(JSObject object) {
        HashMap<String, String> map = new HashMap<>();

        Iterator<String> keysItr = object.keys();
        while (keysItr.hasNext()) {
            String key = keysItr.next();
            String value = null;
            try {
                value = String.valueOf(object.get(key));
            } catch (JSONException e) {
                e.printStackTrace();
            }
            if (value != null) {
                map.put(key, value);
            }
        }
        return map;
    }

    @Override
    protected void handleOnActivityResult(int requestCode, int resultCode, Intent data) {
        super.handleOnActivityResult(requestCode, resultCode, data);

        PluginCall savedCall = bridge.getSavedCall(callbackID);

        if (savedCall == null) {
            return;
        }

        if (requestCode == CFPaymentService.REQ_CODE && data != null) {
            try {
                JSONObject jsonObject = getJson(data.getExtras());
                JSObject response = JSObject.fromJSONObject(jsonObject);
                savedCall.resolve(response);
                Log.d("CF::SDK::Android", "Response : " + response.toString());
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

    }

    private JSONObject getJson(final Bundle bundle) {
        if (bundle == null) return null;
        JSONObject jsonObject = new JSONObject();

        for (String key : bundle.keySet()) {
            try {
                jsonObject.put(key, wrap(bundle.get(key)));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return jsonObject;
    }

    public static Object wrap(Object o) {
        if (o == null) {
            return JSONObject.NULL;
        }
        if (o instanceof JSONArray || o instanceof JSONObject) {
            return o;
        }
        if (o.equals(JSONObject.NULL)) {
            return o;
        }
        try {
            if (o instanceof Collection) {
                return new JSONArray((Collection) o);
            } else if (o.getClass().isArray()) {
                return toJSONArray(o);
            }
            if (o instanceof Map) {
                return new JSONObject((Map) o);
            }
            if (o instanceof Boolean ||
                    o instanceof Byte ||
                    o instanceof Character ||
                    o instanceof Double ||
                    o instanceof Float ||
                    o instanceof Integer ||
                    o instanceof Long ||
                    o instanceof Short ||
                    o instanceof String) {
                return o;
            }
            if (o.getClass().getPackage().getName().startsWith("java.")) {
                return o.toString();
            }
        } catch (Exception ignored) {
        }
        return null;
    }

    public static JSONArray toJSONArray(Object array) throws JSONException {
        JSONArray result = new JSONArray();
        if (!array.getClass().isArray()) {
            throw new JSONException("Not a primitive array: " + array.getClass());
        }
        final int length = Array.getLength(array);
        for (int i = 0; i < length; ++i) {
            result.put(wrap(Array.get(array, i)));
        }
        return result;
    }
}
