package com.ponzischeme89.memby.screensaver

import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.service.dreams.DreamService
import android.view.KeyEvent
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.lifecycle.setViewTreeViewModelStoreOwner
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.ponzischeme89.memby.ui.player.PlayerActivity
import com.ponzischeme89.memby.ui.screensaver.ScreensaverActions
import com.ponzischeme89.memby.ui.screensaver.ScreensaverContent
import com.ponzischeme89.memby.ui.theme.MembyTheme

/**
 * The Android TV / Google TV system screensaver (Ambient mode source).
 *
 * Hosts the shared [ScreensaverContent] composable inside a [ComposeView]. Because
 * a DreamService is not a ComponentActivity, we supply the ViewTree owners Compose
 * requires via [DreamLifecycleOwner]. The saved Emby session is read from the shared
 * repository/DataStore, so this works with no Activity running and after process death.
 *
 * D-pad navigation, panel toggling and OK-to-act are handled inside the composable
 * (via Compose focus + key events). The remote's Play/Pause media key is intercepted
 * here in [dispatchKeyEvent] and routed to the composable through [ScreensaverActions].
 */
class MembyDreamService : DreamService() {

    private var owner: DreamLifecycleOwner? = null
    private var composeView: ComposeView? = null
    private val actions = ScreensaverActions()
    private val mainHandler = Handler(Looper.getMainLooper())

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        isFullscreen = true
        isInteractive = true
        isScreenBright = true

        val lifecycleOwner = DreamLifecycleOwner().also { it.onCreate() }
        owner = lifecycleOwner

        composeView = ComposeView(this).apply {
            setViewTreeLifecycleOwner(lifecycleOwner)
            setViewTreeViewModelStoreOwner(lifecycleOwner)
            setViewTreeSavedStateRegistryOwner(lifecycleOwner)
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                MembyTheme {
                    ScreensaverContent(
                        onPlay = { url, title -> launchPlayback(url, title) },
                        onExit = { finish() },
                        actions = actions,
                    )
                }
            }
        }
        setContentView(composeView)

        lifecycleOwner.onStart()
    }

    override fun onDreamingStarted() {
        super.onDreamingStarted()
        owner?.onResume()
    }

    override fun onDreamingStopped() {
        owner?.onPause()
        super.onDreamingStopped()
    }

    override fun onDetachedFromWindow() {
        // Cancels Compose coroutines (rotation timer, network calls) by disposing
        // the composition, then tears down the owner and releases callbacks.
        actions.playCurrent = null
        mainHandler.removeCallbacksAndMessages(null)
        owner?.onDestroy()
        owner = null
        composeView = null
        super.onDetachedFromWindow()
    }

    /** Route the hardware Play/Pause media key to the current item; delegate the rest. */
    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        if (event.action == KeyEvent.ACTION_DOWN) {
            when (event.keyCode) {
                KeyEvent.KEYCODE_MEDIA_PLAY,
                KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
                    actions.playCurrent?.invoke()
                    return true
                }
            }
        }
        return super.dispatchKeyEvent(event)
    }

    /**
     * Exit the dream, then start playback. Launching the player *after* the dream
     * finishes (via a short main-thread post) avoids the "activity started behind
     * the dream" race some TV builds exhibit. Uses the application context because
     * this service is being torn down.
     */
    private fun launchPlayback(url: String, title: String) {
        val intent = PlayerActivity.intent(applicationContext, url, title)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        finish()
        mainHandler.postDelayed({
            runCatching { applicationContext.startActivity(intent) }
        }, 150)
    }
}

/** Minimal owner bundle so Compose can run inside a DreamService window. */
private class DreamLifecycleOwner : LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {

    private val lifecycleRegistry = LifecycleRegistry(this)
    private val store = ViewModelStore()
    private val savedStateController = SavedStateRegistryController.create(this)

    override val lifecycle: Lifecycle get() = lifecycleRegistry
    override val viewModelStore: ViewModelStore get() = store
    override val savedStateRegistry: SavedStateRegistry get() = savedStateController.savedStateRegistry

    fun onCreate() {
        savedStateController.performRestore(null)
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    fun onStart() = lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
    fun onResume() = lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onPause() = lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)

    fun onDestroy() {
        // Only step down from whatever state we're in; guard against double-destroy.
        if (lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
        }
        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        store.clear()
    }
}
