package hu.bbara.purefin

import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.activity.compose.BackHandler
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.core.tween
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
import androidx.navigation3.runtime.EntryProviderScope
import androidx.navigation3.runtime.NavBackStack
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import coil3.ImageLoader
import coil3.SingletonImageLoader
import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import coil3.request.crossfade
import coil3.util.DebugLogger
import dagger.hilt.android.AndroidEntryPoint
import hu.bbara.purefin.core.data.SessionBootstrapper
import hu.bbara.purefin.core.jellyfin.JellyfinAuthInterceptor
import hu.bbara.purefin.core.data.UserSessionRepository
import hu.bbara.purefin.navigation.LocalHomeMediaSharedBoundsKey
import hu.bbara.purefin.navigation.LocalNavigationBackStack
import hu.bbara.purefin.navigation.LocalNavigationManager
import hu.bbara.purefin.navigation.LocalSetHomeMediaSharedBoundsKey
import hu.bbara.purefin.navigation.LocalSharedTransitionScope
import hu.bbara.purefin.core.navigation.NavigationCommand
import hu.bbara.purefin.core.navigation.NavigationManager
import hu.bbara.purefin.core.navigation.Route
import hu.bbara.purefin.ui.common.dialog.ExitAppDialog
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
import hu.bbara.purefin.ui.screen.login.LoginScreen
import hu.bbara.purefin.ui.theme.AppTheme
import hu.bbara.purefin.ui.theme.backgroundDark
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okio.Path.Companion.toPath
import javax.inject.Inject

@AndroidEntryPoint
class PurefinActivity : ComponentActivity() {

    @Inject
    lateinit var entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<Route>.() -> Unit>
    @Inject
    lateinit var userSessionRepository: UserSessionRepository

    @Inject
    lateinit var navigationManager: NavigationManager

    @Inject
    lateinit var sessionBootstrapper: SessionBootstrapper

    @Inject
    lateinit var authInterceptor: JellyfinAuthInterceptor

    private val imageOkHttpClient: OkHttpClient by lazy {
        OkHttpClient.Builder()
            .addNetworkInterceptor(authInterceptor)
            .build()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch { init() }
        configureImageLoader()
        enableEdgeToEdge()
        setContent {
            AppTheme() {
                MainApp(
                    userSessionRepository = userSessionRepository,
                    entryBuilders = entryBuilders,
                    navigationManager = navigationManager
                )
            }
        }
    }

    private suspend fun init() {
        sessionBootstrapper.initialize()
    }

    private fun configureImageLoader() {
        SingletonImageLoader.setSafe { context ->
            val builder = ImageLoader.Builder(context)
                .components {
                    add(
                        OkHttpNetworkFetcherFactory(
                            callFactory = { imageOkHttpClient }
                        )
                    )
                }
                .memoryCache {
                    MemoryCache.Builder()
                        .maxSizePercent(context, 0.20)
                        .build()
                }
                .diskCache {
                    DiskCache.Builder()
                        .directory(context.cacheDir.resolve("image_cache").absolutePath.toPath())
                        .maxSizeBytes(30000000)
                        .build()
                }
                .crossfade(true)

            if (isDebuggable()) {
                builder.logger(DebugLogger())
            }

            builder.build()
        }
    }

    private fun isDebuggable(): Boolean {
        return (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
    }

    @OptIn(ExperimentalSharedTransitionApi::class)
    @Composable
    fun MainApp(
        userSessionRepository: UserSessionRepository,
        entryBuilders: Set<@JvmSuppressWildcards EntryProviderScope<Route>.() -> Unit>,
        navigationManager: NavigationManager
    ) {
        var sessionLoaded by remember { mutableStateOf(false) }
        var showExitDialog by remember { mutableStateOf(false) }
        val isLoggedIn by userSessionRepository.isLoggedIn.collectAsStateWithLifecycle(initialValue = false)

        LaunchedEffect(Unit) {
            userSessionRepository.isLoggedIn.collect {
                sessionLoaded = true
            }
        }

        if (!sessionLoaded) {
            BackHandler {
                showExitDialog = true
            }
            PurefinWaitingScreen(modifier = Modifier.fillMaxSize())
        } else if (isLoggedIn) {
            @Suppress("UNCHECKED_CAST")
            val backStack = rememberNavBackStack(Route.Home) as NavBackStack<Route>
            var homeMediaSharedBoundsKey by remember { mutableStateOf<String?>(null) }
            val appEntryProvider =
                entryProvider {
                    entryBuilders.forEach { builder -> builder() }
                }

            LaunchedEffect(navigationManager, backStack) {
                navigationManager.commands.collect { command ->
                    when (command) {
                        NavigationCommand.Pop -> {
                            if (backStack.size > 1) {
                                backStack.removeLastOrNull()
                            } else {
                                showExitDialog = true
                            }
                        }
                        is NavigationCommand.Navigate -> backStack.add(command.route)
                        is NavigationCommand.ReplaceAll -> {
                            backStack.clear()
                            backStack.add(command.route)
                        }
                    }
                }
            }

            BackHandler(enabled = backStack.size == 1) {
                showExitDialog = true
            }

            SharedTransitionLayout {
                CompositionLocalProvider(
                    LocalNavigationManager provides navigationManager,
                    LocalNavigationBackStack provides backStack.toList(),
                    LocalSharedTransitionScope provides this,
                    LocalHomeMediaSharedBoundsKey provides homeMediaSharedBoundsKey,
                    LocalSetHomeMediaSharedBoundsKey provides { key ->
                        homeMediaSharedBoundsKey = key
                    }
                ) {
                    NavDisplay(
                        backStack = backStack,
                        onBack = {
                            if (backStack.size > 1) {
                                navigationManager.pop()
                            } else {
                                showExitDialog = true
                            }
                        },
                        modifier = Modifier
                            .fillMaxSize()
                            .background(backgroundDark)
                            .semantics { testTagsAsResourceId = true },
                        transitionSpec = {
                            fadeIn(
                                animationSpec = tween(
                                    durationMillis = 140
                                )
                            ) togetherWith
                                fadeOut(animationSpec = tween(durationMillis = 140))
                        },
                        popTransitionSpec = {
                            fadeIn(
                                animationSpec = tween(
                                    durationMillis = 140
                                )
                            ) togetherWith
                                fadeOut(animationSpec = tween(durationMillis = 140))
                        },
                        predictivePopTransitionSpec = { _ ->
                            fadeIn(
                                animationSpec = tween(
                                    durationMillis = 140
                                )
                            ) togetherWith
                                fadeOut(animationSpec = tween(durationMillis = 140))
                        },
                        entryDecorators =
                            listOf(
                                rememberSaveableStateHolderNavEntryDecorator(),
                                rememberViewModelStoreNavEntryDecorator(),
                            ),
                        entryProvider = appEntryProvider
                    )
                }
            }
        } else {
            BackHandler {
                showExitDialog = true
            }
            LoginScreen(
                modifier = Modifier.semantics { testTagsAsResourceId = true }
            )
        }

        if (showExitDialog) {
            ExitAppDialog(
                onCloseConfirmed = { finishAndRemoveTask() },
                onDismiss = { showExitDialog = false }
            )
        }
    }
}
