package com.cloudflare.realtimekit;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.view.WindowManager;

import com.cloudflare.realtimekit.ForegroundService;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import java.io.Serializable;
import java.util.HashMap;

import com.cloudflare.realtimekit.RTKHolder;

/*
 * RTKHelper here was renamed from RTKHelperModule to match the module name returned from getName()
 * Keeping different namings in React Module definition & getName() breaks in Expo with newArchitecture enabled
*/
@ReactModule(name = "RTKHelper")
public class RTKHelperModule extends ReactContextBaseJavaModule {

    private final ReactApplicationContext reactContext;
    private static final String ERROR_INVALID_ACTIVITY = "E_INVALID_ACTIVITY";

    // react-native-background-timer start
    private Handler handler;
    private ReactContext reactContext2;
    private Runnable runnable;
    private PowerManager powerManager;
    private PowerManager.WakeLock wakeLock;
    private int existingOrientation;
    private boolean isForcedLandscapeToggled;
    private final LifecycleEventListener listener = new LifecycleEventListener() {
        @Override
        public void onHostResume() {
        }

        @Override
        public void onHostPause() {
        }

        @Override
        public void onHostDestroy() {
            if (wakeLock.isHeld())
                wakeLock.release();
            Intent intent = new Intent(getReactApplicationContext(), ForegroundService.class);
            intent.setAction(Constants.ACTION_FOREGROUND_SERVICE_STOP);
            getReactApplicationContext().stopService(intent);
        }
    };
    // react-native-background-timer end

    private class Request {

        public boolean[] rationaleStatuses;
        public Callback callback;

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

    public RTKHelperModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        this.powerManager = (PowerManager) getReactApplicationContext().getSystemService(reactContext.POWER_SERVICE);
        this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "realtimekit:wakelock");
        reactContext.addLifecycleEventListener(listener);
    }

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

    @ReactMethod
    public void createNotificationChannel(Promise promise) {
        NotificationHelper.getInstance(getReactApplicationContext()).createNotificationChannel(promise);
    }

    @ReactMethod
    public void startService(Promise promise) {
        ComponentName mediaService;
        RTKHolder.currActivity = this.getCurrentActivity();
        Intent intent = new Intent(getReactApplicationContext(), ForegroundService.class);
        intent.setAction(Constants.ACTION_FOREGROUND_SERVICE_START);
        try{
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                mediaService = getReactApplicationContext().startForegroundService(intent);
            } else {
                mediaService = getReactApplicationContext().startService(intent);
            } 
        } catch (RuntimeException e) {
            // Avoid crashing due to ForegroundServiceStartNotAllowedException (API level 31).
            // See: https://developer.android.com/guide/components/foreground-services#background-start-restrictions
            RTKLogger.w("RTKHelper", "Media projection service not started: " + e.getMessage());
            return;
        }
        if (mediaService != null) {
            promise.resolve(null);
        } else {
            promise.reject(Constants.ERROR_SERVICE_ERROR, "RTKForegroundService: Foreground service is not started");
        }
    }

    @ReactMethod
    public void stopService(Promise promise) {
        Intent intent = new Intent(getReactApplicationContext(), ForegroundService.class);
        intent.setAction(Constants.ACTION_FOREGROUND_SERVICE_STOP);
        boolean stopped = getReactApplicationContext().stopService(intent);
        if (stopped) {
            promise.resolve(null);
        } else {
            promise.reject(Constants.ERROR_SERVICE_ERROR, "RTKForegroundService: Foreground service failed to stop");
        }
    }
    @ReactMethod
    private void isForegroundServiceRunning(Promise promise) {
        ActivityManager manager = (ActivityManager) this.getCurrentActivity().getSystemService(getReactApplicationContext().ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if ((ForegroundService.class).getName().equals(service.service.getClassName())) {
                promise.resolve(true);
                return;
            }
        }
        promise.resolve(false);
    }

    @ReactMethod
    public void activateScreenWake() {
        final Activity activity = getCurrentActivity();

        if (activity != null) {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                }
            });
        }
    }

    @ReactMethod
    public void deactivateScreenWake() {
        final Activity activity = getCurrentActivity();

        if (activity != null) {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                }
            });
        }
    }

    @ReactMethod
    public void forceLandscape() {
        final Activity activity = getCurrentActivity();

        if (activity != null) {
            this.isForcedLandscapeToggled = true;
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    existingOrientation = activity.getRequestedOrientation();
                    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                }
            });
        }
    }

    @ReactMethod
    public void forcePotrait() {
        final Activity activity = getCurrentActivity();

        if (activity != null) {
            this.isForcedLandscapeToggled = true;
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    existingOrientation = activity.getRequestedOrientation();
                    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                }
            });
        }
    }

    @ReactMethod
    public void resetOrientation() {
        final Activity activity = getCurrentActivity();

        if (activity != null) {
            this.isForcedLandscapeToggled = false;
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    activity.setRequestedOrientation(existingOrientation);
                }
            });
        }
    }

    @ReactMethod
    public void isForcedLandscape(Promise p) {
        p.resolve(this.isForcedLandscapeToggled);
    }

    @ReactMethod
    public void backgroundTimerStart(final int delay) {
        if (!wakeLock.isHeld())
            wakeLock.acquire();

        handler = new Handler();
        runnable = new Runnable() {
            @Override
            public void run() {
                backgroundTimerSendEvent(reactContext, "backgroundTimer");
            }
        };

        handler.post(runnable);
    }

    @ReactMethod
    public void backgroundTimerStop() {
        if (wakeLock.isHeld())
            wakeLock.release();

        // avoid null pointer exceptio when stop is called without start
        if (handler != null)
            handler.removeCallbacks(runnable);
    }

    private void backgroundTimerSendEvent(ReactContext reactContext, String eventName) {
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, null);
    }

    @ReactMethod
    public void backgroundTimerSetTimeout(final int id, final double timeout) {
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (getReactApplicationContext().hasActiveCatalystInstance()) {
                    getReactApplicationContext()
                            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                            .emit("backgroundTimer.timeout", id);
                }
            }
        }, (long) timeout);
    }
}
