package expo.modules.devlauncher.launcher.configurators

import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.content.pm.ActivityInfo
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Build
import android.util.Log
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
import androidx.annotation.UiThread
import androidx.core.view.ViewCompat
import com.facebook.react.ReactActivity
import expo.modules.devlauncher.helpers.RGBAtoARGB
import expo.modules.devlauncher.helpers.isValidColor
import expo.modules.devlauncher.launcher.manifest.DevLauncherNavigationBarStyle
import expo.modules.devlauncher.launcher.manifest.DevLauncherNavigationBarVisibility
import expo.modules.devlauncher.launcher.manifest.DevLauncherOrientation
import expo.modules.devlauncher.launcher.manifest.DevLauncherStatusBarStyle
import expo.modules.manifests.core.Manifest

class DevLauncherExpoActivityConfigurator(
  private var manifest: Manifest,
  private val context: Context
) {
  fun applyTaskDescription(activity: Activity) {
    if (!isValidColor(manifest.getPrimaryColor())) {
      return
    }
    val color = Color.parseColor(manifest.getPrimaryColor())
    val icon = BitmapFactory.decodeResource(context.resources, context.applicationInfo.icon)

    activity.setTaskDescription(
      ActivityManager.TaskDescription(
        manifest.getName(),
        icon,
        color
      )
    )
  }

  fun applyOrientation(activity: ReactActivity) {
    when (manifest.getOrientation()) {
      DevLauncherOrientation.DEFAULT -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
      DevLauncherOrientation.PORTRAIT -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
      DevLauncherOrientation.LANDSCAPE -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
      else -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
    }
  }

  fun applyStatusBarConfiguration(activity: ReactActivity) {
    val statusBarOptions = manifest.getAndroidStatusBarOptions()
    val style = statusBarOptions?.optString("barStyle")
    val backgroundColor = statusBarOptions?.optString("backgroundColor")
    val translucent = statusBarOptions == null || statusBarOptions.optBoolean("translucent", true)
    val hidden = statusBarOptions != null && statusBarOptions.optBoolean("hidden", false)

    activity.runOnUiThread {
      // clear android:windowTranslucentStatus flag from Window as RN achieves translucency using WindowInsets
      activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
      setHidden(hidden, activity)
      setTranslucent(translucent, activity)
      val appliedStyle = setStyle(style, activity)

      // Color passed from manifest is in format '#RRGGBB(AA)' and Android uses '#AARRGGBB'
      val normalizedStatusBarBackgroundColor = RGBAtoARGB(backgroundColor)

      val finalBackgroundColor = if (normalizedStatusBarBackgroundColor == null || !isValidColor(normalizedStatusBarBackgroundColor)) {
        // backgroundColor is invalid or not set
        if (appliedStyle == DevLauncherStatusBarStyle.LIGHT) {
          // appliedStatusBarStyle is "light-content" so background color should be semi transparent black
          Color.parseColor("#88000000")
        } else {
          // otherwise it has to be transparent
          Color.TRANSPARENT
        }
      } else {
        Color.parseColor(normalizedStatusBarBackgroundColor)
      }

      setColor(finalBackgroundColor, activity)
    }
  }

  @UiThread
  private fun setStyle(style: String?, activity: Activity): String {
    var appliedStatusBarStyle = DevLauncherStatusBarStyle.LIGHT
    val decorView = activity.window.decorView
    var systemUiVisibilityFlags = decorView.systemUiVisibility
    when (style) {
      DevLauncherStatusBarStyle.LIGHT -> {
        systemUiVisibilityFlags = systemUiVisibilityFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
        appliedStatusBarStyle = DevLauncherStatusBarStyle.LIGHT
      }
      DevLauncherStatusBarStyle.DARK -> {
        systemUiVisibilityFlags = systemUiVisibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        appliedStatusBarStyle = DevLauncherStatusBarStyle.DARK
      }
      else -> {
        systemUiVisibilityFlags = systemUiVisibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        appliedStatusBarStyle = DevLauncherStatusBarStyle.DARK
      }
    }
    decorView.systemUiVisibility = systemUiVisibilityFlags

    return appliedStatusBarStyle
  }

  @UiThread
  private fun setHidden(hidden: Boolean, activity: Activity) {
    if (hidden) {
      activity.window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
      activity.window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
    } else {
      activity.window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
      activity.window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
    }
  }

  @UiThread
  private fun setTranslucent(translucent: Boolean, activity: Activity) {
    // If the status bar is translucent hook into the window insets calculations
    // and consume all the top insets so no padding will be added under the status bar.
    val decorView = activity.window.decorView
    if (translucent) {
      decorView.setOnApplyWindowInsetsListener { v: View, insets: WindowInsets? ->
        val defaultInsets = v.onApplyWindowInsets(insets)
        defaultInsets.replaceSystemWindowInsets(
          defaultInsets.systemWindowInsetLeft,
          0,
          defaultInsets.systemWindowInsetRight,
          defaultInsets.systemWindowInsetBottom
        )
      }
    } else {
      decorView.setOnApplyWindowInsetsListener(null)
    }
    ViewCompat.requestApplyInsets(decorView)
  }

  @UiThread
  fun setColor(color: Int, activity: Activity) {
    activity
      .window
      .addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
    activity
      .window.statusBarColor = color
  }

  fun applyNavigationBarConfiguration(activity: ReactActivity) {
    val navBarOptions = manifest.getAndroidNavigationBarOptions() ?: return

    // Set background color of navigation bar
    val navBarColor = navBarOptions.optString("backgroundColor")
    if (isValidColor(navBarColor)) {
      try {
        activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
        activity.window.navigationBarColor = Color.parseColor(navBarColor)
      } catch (e: Throwable) {
        Log.e(TAG, "Failed to configure androidNavigationBar.backgroundColor", e)
      }
    }

    // Set icon color of navigation bar
    val navBarAppearance = navBarOptions.optString("barStyle")
    if (navBarAppearance != "" && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      try {
        activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
        activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        if (navBarAppearance == DevLauncherNavigationBarStyle.DARK) {
          val decorView = activity.window.decorView
          var flags = decorView.systemUiVisibility
          flags = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
          decorView.systemUiVisibility = flags
        }
      } catch (e: Throwable) {
        Log.e(TAG, "Failed to configure androidNavigationBar.barStyle", e)
      }
    }

    // Set visibility of navigation bar
    val navBarVisible = navBarOptions.optString("visible")
    if (navBarVisible != "") {
      // Hide both the navigation bar and the status bar. The Android docs recommend, "you should
      // design your app to hide the status bar whenever you hide the navigation bar."
      val decorView = activity.window.decorView
      val flags = decorView.systemUiVisibility or when (navBarVisible) {
        DevLauncherNavigationBarVisibility.LEANBACK -> (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN)
        DevLauncherNavigationBarVisibility.IMMERSIVE -> (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE)
        DevLauncherNavigationBarVisibility.STICKY_IMMERSIVE -> (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
        else -> 0
      }
      decorView.systemUiVisibility = flags
    }
  }

  companion object {
    private val TAG = DevLauncherExpoActivityConfigurator::class.java.simpleName
  }
}
