package com.fiuu.xdk.reactnative;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.fiuu.xdk.PaymentActivity;
import com.fiuu.xdk.googlepay.ActivityGP;
import android.content.Intent;

import android.app.Activity;
import android.util.Log;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.BaseActivityEventListener;

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import javax.annotation.Nullable;

public class FiuuPaymentModule extends ReactContextBaseJavaModule {
    private static final String[] REQUIRED_FIELDS = {
            "mp_merchant_ID", "mp_verification_key", "mp_amount", "mp_order_ID"
    };
    private static final java.util.Set<String> PLACEHOLDER_VALUES = new java.util.HashSet<>(Arrays.asList(
            "merchant_id", "verfication_key", "verification_key"));
    private static final java.util.Set<String> BOOLEAN_FIELDS = new java.util.HashSet<>(Arrays.asList(
            "mp_sandbox_mode",
            "mp_extended_vcode",
            "mp_closebutton_display",
            "mp_express_mode",
            "mp_channel_editing",
            "mp_editing_enabled",
            "mp_dev_mode"
    ));

    private static HashMap<String, Object> paymentDetails;
    private static Callback successCallback;
    private static Callback errorCallback;
    private static boolean paymentInFlight = false;
    private static boolean resultDelivered = false;

    private void deliverTransactionResult(@Nullable Intent data, int resultCode) {
        if (resultDelivered) {
            return;
        }

        String transactionResult = null;
        if (data != null) {
            transactionResult = data.getStringExtra(PaymentActivity.XDKTransactionResult);
        }

        if (transactionResult != null && !transactionResult.isEmpty()) {
            TransactionResultHandler.Dispatch dispatch =
                    TransactionResultHandler.classify(transactionResult);
            resultDelivered = true;

            switch (dispatch.route) {
                case SUCCESS:
                    invokeCallbackOnce(successCallback, dispatch.payload);
                    break;
                case PENDING:
                case FAILURE:
                    invokeCallbackOnce(errorCallback, dispatch.payload);
                    break;
                case MALFORMED:
                default:
                    invokeCallbackOnce(errorCallback, dispatch.message);
                    break;
            }
            clearPaymentCallbacks();
            return;
        }

        resultDelivered = true;
        if (resultCode == Activity.RESULT_CANCELED) {
            invokeCallbackOnce(errorCallback, "Transaction canceled or failed. Please try again.");
        } else if (resultCode == Activity.RESULT_OK) {
            invokeCallbackOnce(errorCallback, "Payment completed but no transaction result was returned.");
        } else {
            invokeCallbackOnce(errorCallback, "Payment failed with an unexpected result.");
        }
        clearPaymentCallbacks();
    }

    private static void invokeCallbackOnce(@Nullable Callback callback, Object payload) {
        if (callback == null) {
            return;
        }
        try {
            callback.invoke(payload);
        } catch (RuntimeException ignored) {
            Log.e("FiuuPayment", "Payment callback was already consumed.");
        }
    }

    private static void clearPaymentCallbacks() {
        successCallback = null;
        errorCallback = null;
        paymentInFlight = false;
        if (paymentDetails != null) {
            paymentDetails.clear();
            paymentDetails = null;
        }
    }

    private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
        @Override
        public void onActivityResult(final Activity activity, int requestCode, int resultCode, Intent data) {
            if (requestCode == PaymentActivity.XDKResponseCode) {
                deliverTransactionResult(data, resultCode);
            }
        }
    };

    /**
     * Validates that the minimum required credentials/details are present before
     * handing off to the native payment SDK. Missing/placeholder values here are
     * the most common cause of the SDK crashing deep in its own Activity instead
     * of surfacing a catchable error, so we fail fast with a clear message.
     */
    private static String validateRequiredFields(JSONObject obj) {
        List<String> missing = new ArrayList<>();
        for (String field : REQUIRED_FIELDS) {
            String value = obj.optString(field, "").trim();
            if (value.isEmpty() || PLACEHOLDER_VALUES.contains(value.toLowerCase(Locale.ROOT))) {
                missing.add(field);
            }
        }
        if (missing.isEmpty()) {
            return null;
        }
        return "Missing or invalid required payment field(s): " + String.join(", ", missing)
                + ". Provide valid merchant credentials and a unique mp_order_ID for each payment attempt.";
    }

    private static boolean isBooleanField(String key) {
        return BOOLEAN_FIELDS.contains(key);
    }

    @Override
    public String getName() {
        return "FiuuPayment";
    }

    public FiuuPaymentModule(ReactApplicationContext reactContext) {
        super(reactContext);
        reactContext.addActivityEventListener(mActivityEventListener);
    }

    @ReactMethod
    public void setPaymentDetails(String str, @Nullable Callback successCallback, @Nullable Callback errorCallback) {
        if (paymentInFlight) {
            if (errorCallback != null) {
                errorCallback.invoke("A payment is already in progress.");
            }
            return;
        }

        if (getCurrentActivity() == null) {
            if (errorCallback != null) {
                errorCallback.invoke("Unable to start payment: no active Activity was found.");
            } else {
                Log.e("FiuuPayment", "Unable to start payment: no active Activity was found, and no errorCallback was registered.");
            }
            return;
        }

        try {
            JSONObject obj = new JSONObject(str);

            String validationError = validateRequiredFields(obj);
            if (validationError != null) {
                if (errorCallback != null) {
                    errorCallback.invoke(validationError);
                } else {
                    Log.e("FiuuPayment", validationError);
                }
                return;
            }

            String paymentModeError = PaymentModeHandler.validate(obj);
            if (paymentModeError != null) {
                if (errorCallback != null) {
                    errorCallback.invoke(paymentModeError);
                } else {
                    Log.e("FiuuPayment", paymentModeError);
                }
                return;
            }

            String paymentMode = PaymentModeHandler.normalize(obj.optString(PaymentModeHandler.FIELD, ""));
            HashMap<String, Object> details = new HashMap<>();

            Iterator<String> iter = obj.keys();
            while (iter.hasNext()) {
                String key = iter.next();
                if (PaymentModeHandler.FIELD.equals(key)) {
                    continue;
                }
                Object value = obj.get(key);

                if (isBooleanField(key)) {
                    details.put(key, PaymentFieldNormalizer.parseBoolean(value));
                } else if (value instanceof JSONArray) {
                    JSONArray array = (JSONArray) value;
                    String[] stringArray = new String[array.length()];
                    for (int i = 0; i < array.length(); i++) {
                        stringArray[i] = array.getString(i);
                    }
                    details.put(key, stringArray);
                } else if (value instanceof String) {
                    details.put(key, (String) value);
                } else {
                    details.put(key, value.toString());
                }
            }
            details.put("is_submodule", true);
            details.put("module_id", "fiuu-mobile-xdk-reactnative");
            details.put("wrapper_version", BuildConfig.WRAPPER_VERSION);

            Intent intent;
            if (PaymentModeHandler.isGooglePay(paymentMode)) {
                intent = new Intent(getCurrentActivity(), ActivityGP.class);
            } else {
                intent = new Intent(getCurrentActivity(), PaymentActivity.class);
            }
            intent.putExtra(PaymentActivity.XDKPaymentDetails, details);

            paymentDetails = details;
            FiuuPaymentModule.successCallback = successCallback;
            FiuuPaymentModule.errorCallback = errorCallback;
            resultDelivered = false;
            paymentInFlight = true;
            getCurrentActivity().startActivityForResult(intent, PaymentActivity.XDKResponseCode);
        } catch (Exception e) {
            clearPaymentCallbacks();
            String message = e.getMessage() != null ? e.getMessage() : "Unknown error occurred while starting payment.";
            if (errorCallback != null) {
                errorCallback.invoke(message);
            } else {
                Log.e("FiuuPayment", "Payment failed and no errorCallback was registered", e);
            }
        }
    }

}
