package com.cloudflare.realtimekit;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.SparseArray;
import android.os.Process;
import android.util.Log;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.Callback;
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.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.modules.core.PermissionAwareActivity;
import java.util.ArrayList;

public class RNPermissionsModule extends ReactContextBaseJavaModule implements PermissionListener {

    private final ReactApplicationContext reactContext;

    private final SparseArray<Request> mRequests = new SparseArray<Request>();
    private final String GRANTED = "granted";
    private final String DENIED = "denied";
    private final String UNAVAILABLE = "unavailable";
    private final String BLOCKED = "blocked";
    private final String PROMPTED = "prompted";
    private int mRequestCode = 0;
    private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";


    private class Request {

        public boolean[] rationaleStatuses;
        public Callback callback;

        public Request(boolean[] rationaleStatuses, Callback callback) {
            this.rationaleStatuses = rationaleStatuses;
            this.callback = callback;
        }
    }

    public RNPermissionsModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
    }

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


    private @Nullable
    String getFieldName(final String permission) {
        if (permission.equals("android.permission.CAMERA"))
            return "CAMERA";
        if (permission.equals("android.permission.BLUETOOTH"))
            return "BLUETOOTH";
        if (permission.equals("android.permission.BLUETOOTH_CONNECT"))
            return "BLUETOOTH_CONNECT";
        if (permission.equals("android.permission.READ_EXTERNAL_STORAGE"))
            return "READ_EXTERNAL_STORAGE";
        if (permission.equals("android.permission.READ_PHONE_STATE"))
            return "READ_PHONE_STATE";
        if (permission.equals("android.permission.RECORD_AUDIO"))
            return "RECORD_AUDIO";
        if (permission.equals("android.permission.USE_SIP"))
            return "USE_SIP";
        if (permission.equals("android.permission.WRITE_EXTERNAL_STORAGE"))
            return "WRITE_EXTERNAL_STORAGE";
        if (permission.equals("android.permission.ACCESS_FINE_LOCATION"))
            return "ACCESS_FINE_LOCATION";
        return null;
    }

    private boolean permissionExists(final String permission) {
        String fieldName = getFieldName(permission);
        if (fieldName == null)
            return false;

        try {
            Manifest.permission.class.getField(fieldName);
            return true;
        } catch (NoSuchFieldException ignored) {
            return false;
        }
    }

    @ReactMethod
    public void checkMultiplePermissions(final ReadableArray permissions, final Promise promise) {
        final WritableMap output = new WritableNativeMap();
        final ArrayList<String> permissionsToCheck = new ArrayList<String>();
        int checkedPermissionsCount = 0;

        Context context = getReactApplicationContext().getBaseContext();

        for (int i = 0; i < permissions.size(); i++) {
            String permission = permissions.getString(i);

            if (!permissionExists(permission)) {
                output.putString(permission, UNAVAILABLE);
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                output.putString(
                        permission,
                        context.checkPermission(permission, Process.myPid(), Process.myUid())
                                == PackageManager.PERMISSION_GRANTED
                                ? GRANTED
                                : BLOCKED);
            } else if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                int x = context.checkSelfPermission(permission);
                output.putString(permission, GRANTED);
            } else {
                output.putString(permission, PROMPTED);
            }
        }

        promise.resolve(output);
        return;
    }

    @ReactMethod
    public void requestMultiplePermissions(final ReadableArray permissions, boolean entryFlag, final Promise promise) {
        final WritableMap output = new WritableNativeMap();
        final ArrayList<String> permissionsToCheck = new ArrayList<String>();
        int checkedPermissionsCount = 0;

        Context context = getReactApplicationContext().getBaseContext();

        for (int i = 0; i < permissions.size(); i++) {
            String permission = permissions.getString(i);

            if (!permissionExists(permission)) {
                output.putString(permission, UNAVAILABLE);
                checkedPermissionsCount++;
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                output.putString(
                        permission,
                        context.checkPermission(permission, Process.myPid(), Process.myUid())
                                == PackageManager.PERMISSION_GRANTED
                                ? GRANTED
                                : BLOCKED);
                checkedPermissionsCount++;
            } else if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
                output.putString(permission, GRANTED);
                checkedPermissionsCount++;
            } else if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
                output.putString(permission, DENIED);
                permissionsToCheck.add(permission);
                checkedPermissionsCount++;
            } else {
                permissionsToCheck.add(permission);
            }
        }
        if (permissions.size() == checkedPermissionsCount && permissionsToCheck.size() == 0) {
            promise.resolve(output);
            return;
        }
        try {
            Activity act = getCurrentActivity();
            boolean[] rationaleStatuses = new boolean[permissions.size()];

            mRequests.put(mRequestCode, new Request(
                    rationaleStatuses,
                    new Callback() {
                        @Override
                        public void invoke(Object... args) {
                            int[] results = (int[]) args[0];

                            for (int j = 0; j < permissionsToCheck.size(); j++) {
                                String permission = permissionsToCheck.get(j);

                                if (results.length > 0 && results[j] == PackageManager.PERMISSION_GRANTED) {
                                    output.putString(permission, GRANTED);
                                } else {
                                    output.putString(permission, DENIED);
                                }
                            }
                            if(promise != null && output!=null)
                            promise.resolve(output);
                        }
                    }));

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                try {
                    if (!(act instanceof PermissionAwareActivity)) {
                        act.requestPermissions(permissionsToCheck.toArray(new String[0]), mRequestCode);
                    } else {
                        PermissionAwareActivity ac = (PermissionAwareActivity) act;
                        ac.requestPermissions(permissionsToCheck.toArray(new String[0]), mRequestCode, this);
                    }
                } catch (Error e) {
                    promise.reject(ERROR_INVALID_ACTIVITY, e);
                }
            }
            mRequestCode++;
        } catch (Exception e) {
            promise.reject(ERROR_INVALID_ACTIVITY, e);
        }
    }

    public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        try {
            if(mRequests == null) {
                return false;
            }
            Request request = mRequests.get(requestCode, null);
            if(request == null) {
                return false;
            }
            request.callback.invoke(grantResults);
            mRequests.remove(requestCode);
            return mRequests.size() == 0;
        } catch(Exception e) {
            return false;
        }
    }
}
