package com.rohan.app.ask;

import android.app.Activity;
import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.Size;
import android.util.Log;


import com.rohan.app.ask.annotations.AskDenied;
import com.rohan.app.ask.annotations.AskGranted;
import com.rohan.app.ask.annotations.AskGrantedAll;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

@SuppressWarnings("unused")
public class Ask {

    private String[] permissions;
    private String[] rationalMessages;
    private static final String TAG = Ask.class.getSimpleName();
    private static final String ALL_PERMISSIONS="All";
    private static Permission permissionObj;
    private static Fragment fragment;
    private static Activity activity;
    private static int id;
    private static Map<String, Method> permissionMethodMap;
    private static boolean debug = false;


    private Ask() {
        permissionMethodMap = new HashMap<>();
        debug = false;
        permissionObj = null;
        Random rand = new Random();
        id = rand.nextInt();
    }

    public static Ask on(Activity lActivity) {
        if (lActivity == null) {
            throw new IllegalArgumentException("Null Fragment Reference");
        }
        activity = lActivity;
        return new Ask();
    }

    public static Ask on(Fragment lFragment) {
        if (lFragment == null) {
            throw new IllegalArgumentException("Null Fragment Reference");
        }
        fragment = lFragment;
        activity = lFragment.getActivity();
        return new Ask();
    }

    public Ask forPermissions(@NonNull @Size(min = 1) String... permissions) {
        if (permissions == null || permissions.length == 0) {
            throw new IllegalArgumentException("The permissions to request are missing");
        }
        this.permissions = permissions;
        return this;
    }

    public Ask withRationales(@NonNull String... rationalMessages) {
        if (rationalMessages == null || rationalMessages.length == 0) {
            throw new IllegalArgumentException("The Rationale Messages are missing");
        }
        this.rationalMessages = rationalMessages;
        return this;
    }

    public Ask debug(boolean lDebug) {
        debug = lDebug;
        return this;
    }

    public Ask id(int lId) {
        id = lId;
        return this;
    }

    public void go() {
        if (debug) {
            Log.d(TAG, "request id :: " + id);
        }
        getAnnotatedMethod();
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            if (permissionObj != null) {
                permissionObj.granted(Arrays.asList(permissions));
                permissionObj.denied(new ArrayList<String>());
            }
            for (String permission : permissions) {
                invokeMethod(permission, true);
            }
        } else {
            Intent intent = new Intent(activity, AskActivity.class);
            intent.putExtra(Constants.PERMISSIONS, permissions);
            intent.putExtra(Constants.RATIONAL_MESSAGES, rationalMessages);
            intent.putExtra(Constants.REQUEST_ID, id);
            activity.startActivity(intent);
        }
    }

    public Ask when(@Nullable Permission permission) {
        permissionObj = permission;
        return this;
    }

    public interface Permission {
        void granted(List<String> permissions);

        void denied(List<String> permissions);

        void grantedAll();
    }

    public static class Receiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context lContext, Intent intent) {
            boolean grantedAll=true;
            int requestId = intent.getIntExtra(Constants.REQUEST_ID, 0);
            if (debug) {
                Log.d(TAG, "request id :: " + id + ",  received request id :: " + requestId);
            }

            if (id != requestId) {
                return;
            }
            String[] permissions = intent.getStringArrayExtra(Constants.PERMISSIONS);
            int[] grantResults = intent.getIntArrayExtra(Constants.GRANT_RESULTS);
            List<String> grantedPermissions = new ArrayList<>();
            List<String> deniedPermissions = new ArrayList<>();
            for (int i = 0; i < permissions.length; i++) {
                boolean isGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
                invokeMethod(permissions[i], isGranted);
                if (isGranted) {
                    grantedPermissions.add(permissions[i]);
                } else {
                    deniedPermissions.add(permissions[i]);
                    grantedAll=false;
                }
            }
            //if all permissions are granted
            if(grantedAll){
                invokeMethod(ALL_PERMISSIONS,true);
            }
            if (permissionObj != null) {
                permissionObj.denied(deniedPermissions);
                permissionObj.granted(grantedPermissions);
                if(deniedPermissions.size()==0)
                    permissionObj.grantedAll();
            }
        }
    }

    private static void getAnnotatedMethod() {

        permissionMethodMap.clear();
        Method[] methods = fragment != null ? fragment.getClass().getMethods() : activity.getClass().getMethods();
        for (Method method : methods) {
            AskDenied askDenied = method.getAnnotation(AskDenied.class);
            AskGranted askGranted = method.getAnnotation(AskGranted.class);
            AskGrantedAll askGrantedAll=method.getAnnotation(AskGrantedAll.class);
            if (askDenied != null) {
                int lId = askDenied.id() != -1 ? askDenied.id() : id;
                permissionMethodMap.put(false + "_" + askDenied.value() + "_" + id, method);
            }
            if (askGranted != null) {
                int lId = askGranted.id() != -1 ? askGranted.id() : id;
                permissionMethodMap.put(true + "_" + askGranted.value() + "_" + id, method);
            }
            if(askGrantedAll!=null){
                int lId = askGrantedAll.id() != -1 ? askGrantedAll.id() : id;
                permissionMethodMap.put(true + "_" + askGrantedAll.value() + "_" + id, method);
            }
        }
        if (debug) {
            Log.d(TAG, "annotated methods map :: " + permissionMethodMap);
        }
    }

    private static void invokeMethod(String permission, boolean isGranted) {
        String key = isGranted + "_" + permission + "_" + id;
        String val = isGranted ? "Granted" : "Denied";
        try {
            if (debug) {
                Log.d(TAG, "invoke method for key :: " + key);
            }
            if (permissionMethodMap.containsKey(key)) {
                permissionMethodMap.get(key).invoke(fragment != null ? fragment : activity);
            } else if (debug) {
                Log.w(TAG, "No method found to handle the " + permission + " " + val + " case. Please check for the detail here https://github.com/00ec454/Ask");
            }
        } catch (Exception e) {
            if (debug)
                Log.e(TAG, e.getMessage(), e);
        }
    }
}