package com.dtourolle.jellytau

import android.content.Context
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import android.webkit.WebSettings
import android.webkit.WebView
import android.view.View
import androidx.activity.enableEdgeToEdge

class MainActivity : TauriActivity() {
  private val handler = Handler(Looper.getMainLooper())
  private var configAttempts = 0
  private val maxConfigAttempts = 10
  private var audioFocusRequest: AudioFocusRequest? = null
  private val audioManager by lazy { getSystemService(Context.AUDIO_SERVICE) as AudioManager }

  /**
   * Coarse override for whether backgrounding the app should auto-enter PiP.
   *
   * This is NOT what excludes audio/browsing/cast from PiP — that is the
   * PictureInPictureManager.canEnterPip guard, which requires a local video
   * surface to be actively rendering and is re-checked in onUserLeaveHint. This
   * flag is only toggled by the background-audio feature (via
   * AndroidPictureInPicture.setAutoEnterEnabled) so background-audio mode and
   * auto-PiP stay mutually exclusive.
   */
  @Volatile
  private var autoEnterPipEnabled = true

  /**
   * Whether the user armed background-audio mode on the current video (UR-040).
   * When true, leaving the app hands audio off to the native ExoPlayer audio
   * service (frontend-driven) instead of entering PiP, and video decode stops.
   * The frontend sets this via AndroidBackgroundAudio.setEnabled.
   */
  @Volatile
  private var backgroundAudioEnabled = false

  /**
   * The WebView carrying the Svelte UI, cached once found so lifecycle overrides
   * can dispatch DOM events into it (native → frontend signalling).
   */
  private var mediaWebView: WebView? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    enableEdgeToEdge()
    super.onCreate(savedInstanceState)

    // Configure WebView for media playback after Tauri initialization
    handler.postDelayed({
      configureWebViewForMedia()
    }, 500)
  }

  override fun onResume() {
    super.onResume()
    configureWebViewForMedia()
  }

  /**
   * Called when the user leaves the app via Home or the gesture equivalent
   * (but NOT via Back). This is the standard hook for auto-entering PiP so
   * video keeps playing in a floating window instead of being backgrounded.
   *
   * TRACES: UR-041 | IR-026 | DR-053
   */
  override fun onUserLeaveHint() {
    super.onUserLeaveHint()
    // Never enter PiP while background-audio mode is armed — the two are mutually
    // exclusive (the handoff runs from onStop instead).
    if (autoEnterPipEnabled && !backgroundAudioEnabled &&
        PictureInPictureManager.canEnterPip(this)) {
      android.util.Log.d("MainActivity", "User leaving with video active - entering PiP")
      PictureInPictureManager.enterPip(this)
    }
  }

  /**
   * The app is no longer visible (Home, app switch, or screen lock). When
   * background-audio mode is armed, tell the frontend to hand video playback off
   * to the native audio service. onStop (rather than onUserLeaveHint) is used
   * because it fires on screen-lock too, which is the primary use case (UR-040).
   *
   * TRACES: UR-040 | IR-025
   */
  override fun onStop() {
    super.onStop()
    if (backgroundAudioEnabled) {
      dispatchWebEvent("jellytau-background")
    }
  }

  /** The app is visible again — tell the frontend to resume WebView video. */
  override fun onStart() {
    super.onStart()
    if (backgroundAudioEnabled) {
      dispatchWebEvent("jellytau-foreground")
    }
  }

  /**
   * Dispatch a DOM CustomEvent into the WebView (native → frontend). Mirrors the
   * evaluateJavascript pattern already used to unmute video elements. Posted to
   * the WebView thread; safe no-op if the WebView isn't found yet.
   */
  private fun dispatchWebEvent(name: String) {
    val webView = mediaWebView ?: run {
      android.util.Log.w("MainActivity", "dispatchWebEvent('$name'): no WebView")
      return
    }
    webView.post {
      webView.evaluateJavascript(
        "window.dispatchEvent(new CustomEvent('$name'));",
        null
      )
      android.util.Log.d("MainActivity", "Dispatched web event: $name")
    }
  }

  override fun onDestroy() {
    NetworkTypeMonitor.stopWatching(this)
    super.onDestroy()
  }

  override fun onPictureInPictureModeChanged(
    isInPictureInPictureMode: Boolean,
    newConfig: android.content.res.Configuration
  ) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
    android.util.Log.d("MainActivity", "PiP mode changed: $isInPictureInPictureMode")
    PictureInPictureManager.onPipModeChanged(this, isInPictureInPictureMode)
  }

  private fun configureWebViewForMedia() {
    try {
      val webView = findWebView(window.decorView)

      if (webView == null) {
        android.util.Log.w("MainActivity", "WebView not found (attempt ${configAttempts + 1}/$maxConfigAttempts)")
        if (configAttempts < maxConfigAttempts) {
          configAttempts++
          handler.postDelayed({
            configureWebViewForMedia()
          }, 200)
        } else {
          android.util.Log.e("MainActivity", "Failed to find WebView after $maxConfigAttempts attempts")
        }
        return
      }

      android.util.Log.d("MainActivity", "WebView found! Configuring settings...")
      mediaWebView = webView

      // Add JavaScript interface for audio focus control
      webView.addJavascriptInterface(object : Any() {
        @JavascriptInterface
        fun requestAudioFocus() {
          handler.post { this@MainActivity.requestAudioFocus() }
        }

        @JavascriptInterface
        fun abandonAudioFocus() {
          handler.post { this@MainActivity.abandonAudioFocus() }
        }
      }, "AndroidAudioFocus")
      android.util.Log.d("MainActivity", "JavaScript interface 'AndroidAudioFocus' added")

      // Add JavaScript interface for picture-in-picture control.
      // enterPip/canEnterPip must run on the main thread; @JavascriptInterface
      // methods are invoked on a WebView binder thread.
      webView.addJavascriptInterface(object : Any() {
        @JavascriptInterface
        fun enterPip() {
          handler.post { PictureInPictureManager.enterPip(this@MainActivity) }
        }

        /** Whether the PiP button should be offered in the player UI at all. */
        @JavascriptInterface
        fun isSupported(): Boolean {
          return PictureInPictureManager.isPipSupported(this@MainActivity)
        }

        /** Whether entering PiP would work right now (local video playing). */
        @JavascriptInterface
        fun canEnterPip(): Boolean {
          return PictureInPictureManager.canEnterPip(this@MainActivity)
        }

        /** Let the frontend opt out of auto-PiP (e.g. while casting). */
        @JavascriptInterface
        fun setAutoEnterEnabled(enabled: Boolean) {
          autoEnterPipEnabled = enabled
        }
      }, "AndroidPictureInPicture")
      android.util.Log.d("MainActivity", "JavaScript interface 'AndroidPictureInPicture' added")

      // Add JavaScript interface for background-audio mode (UR-040). The frontend
      // arms/disarms it via the player toggle; the Activity uses the flag in its
      // lifecycle overrides to decide between the audio handoff and PiP.
      webView.addJavascriptInterface(object : Any() {
        /** Frontend arms/disarms background-audio mode for the current video. */
        @JavascriptInterface
        fun setEnabled(enabled: Boolean) {
          backgroundAudioEnabled = enabled
          android.util.Log.d("MainActivity", "backgroundAudioEnabled = $enabled")
        }

        /** Whether background audio is available on this device (needs PiP-era APIs unnecessary; audio service always present on Android). */
        @JavascriptInterface
        fun isSupported(): Boolean = true
      }, "AndroidBackgroundAudio")
      android.util.Log.d("MainActivity", "JavaScript interface 'AndroidBackgroundAudio' added")

      // Network transport reporting for the WiFi-only download gate (UR-053).
      // The frontend polls these on demand and re-pumps the download queue when
      // the 'jellytau-network-changed' event fires.
      webView.addJavascriptInterface(object : Any() {
        /** Active transport: wifi | ethernet | cellular | other | none | unknown. */
        @JavascriptInterface
        fun currentType(): String = NetworkTypeMonitor.currentType(this@MainActivity)

        /** Whether the active network is unmetered. */
        @JavascriptInterface
        fun isUnmetered(): Boolean = NetworkTypeMonitor.isUnmetered(this@MainActivity)

        /** Whether downloads may run given the wifi-only preference. */
        @JavascriptInterface
        fun isAcceptable(wifiOnly: Boolean): Boolean =
          NetworkTypeMonitor.isAcceptable(this@MainActivity, wifiOnly)

        /** Whether native network detection is available at all (false on non-Android). */
        @JavascriptInterface
        fun isSupported(): Boolean = true
      }, "AndroidNetworkType")
      android.util.Log.d("MainActivity", "JavaScript interface 'AndroidNetworkType' added")

      // Push network changes into the WebView so a queue blocked on "waiting for
      // WiFi" resumes the moment an acceptable network appears.
      NetworkTypeMonitor.startWatching(this) {
        dispatchWebEvent("jellytau-network-changed")
      }

      // Set WebChromeClient to handle video playback and audio focus
      webView.webChromeClient = object : WebChromeClient() {
        override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
          super.onShowCustomView(view, callback)
          android.util.Log.d("MainActivity", "Video entered fullscreen")
        }

        override fun onHideCustomView() {
          super.onHideCustomView()
          android.util.Log.d("MainActivity", "Video exited fullscreen")
        }
      }
      android.util.Log.d("MainActivity", "WebChromeClient configured")

      webView.settings.apply {
        // CRITICAL: Enable media playback without user gesture requirement
        mediaPlaybackRequiresUserGesture = false
        android.util.Log.d("MainActivity", "Set mediaPlaybackRequiresUserGesture = false")

        javaScriptEnabled = true
        domStorageEnabled = true
        allowFileAccess = true
        allowContentAccess = true
        mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW

        android.util.Log.d("MainActivity", "WebView fully configured for media playback")
      }

      // Execute JavaScript to ensure any video elements are unmuted and request audio focus
      webView.post {
        webView.evaluateJavascript("""
          (function() {
            console.log('[Android] Ensuring video elements are unmuted');
            const videos = document.getElementsByTagName('video');
            for (let video of videos) {
              video.muted = false;
              video.volume = 1.0;
              console.log('[Android] Video unmuted, volume:', video.volume, 'muted:', video.muted);

              // Add event listeners to manage audio focus
              video.addEventListener('play', function() {
                console.log('[Android] Video play event - requesting audio focus');
                if (typeof AndroidAudioFocus !== 'undefined') {
                  AndroidAudioFocus.requestAudioFocus();
                }
                console.log('[Android] Video state - muted:', this.muted, 'volume:', this.volume);
              });

              video.addEventListener('pause', function() {
                console.log('[Android] Video pause event - abandoning audio focus');
                if (typeof AndroidAudioFocus !== 'undefined') {
                  AndroidAudioFocus.abandonAudioFocus();
                }
              });

              video.addEventListener('ended', function() {
                console.log('[Android] Video ended event - abandoning audio focus');
                if (typeof AndroidAudioFocus !== 'undefined') {
                  AndroidAudioFocus.abandonAudioFocus();
                }
              });

              video.addEventListener('volumechange', function() {
                console.log('[Android] Video volume changed - volume:', this.volume, 'muted:', this.muted);
              });
            }

            // Monitor for new video elements
            const observer = new MutationObserver(() => {
              const videos = document.getElementsByTagName('video');
              for (let video of videos) {
                if (video.muted) {
                  video.muted = false;
                  video.volume = 1.0;
                  console.log('[Android] New video found and unmuted');
                }
              }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            console.log('[Android] Video unmute observer installed');
          })();
        """.trimIndent(), null)
      }

    } catch (e: Exception) {
      android.util.Log.e("MainActivity", "Failed to configure WebView for media", e)
    }
  }

  private fun findWebView(view: android.view.View): WebView? {
    if (view is WebView) {
      android.util.Log.d("MainActivity", "Found WebView!")
      return view
    }

    if (view is android.view.ViewGroup) {
      for (i in 0 until view.childCount) {
        val child = view.getChildAt(i)
        val webView = findWebView(child)
        if (webView != null) {
          return webView
        }
      }
    }

    return null
  }

  private fun requestAudioFocus() {
    android.util.Log.d("MainActivity", "Requesting audio focus for video playback")

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      val audioAttributes = AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
        .build()

      audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(audioAttributes)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener { focusChange ->
          android.util.Log.d("MainActivity", "Audio focus changed: $focusChange")
        }
        .build()

      val result = audioManager.requestAudioFocus(audioFocusRequest!!)
      android.util.Log.d("MainActivity", "Audio focus request result: $result")
    } else {
      @Suppress("DEPRECATION")
      val result = audioManager.requestAudioFocus(
        { focusChange ->
          android.util.Log.d("MainActivity", "Audio focus changed: $focusChange")
        },
        AudioManager.STREAM_MUSIC,
        AudioManager.AUDIOFOCUS_GAIN
      )
      android.util.Log.d("MainActivity", "Audio focus request result (legacy): $result")
    }
  }

  private fun abandonAudioFocus() {
    android.util.Log.d("MainActivity", "Abandoning audio focus")

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      audioFocusRequest?.let {
        audioManager.abandonAudioFocusRequest(it)
      }
    } else {
      @Suppress("DEPRECATION")
      audioManager.abandonAudioFocus { }
    }
  }
}
