package com.zaojiao.app.core.nav

import androidx.navigation.NavController
import androidx.navigation.NavOptionsBuilder
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onSubscription
import java.util.concurrent.CompletableFuture

object LJGNavigator {
    private val navigationCommands =
        MutableSharedFlow<NavigationCommand>(extraBufferCapacity = Int.MAX_VALUE)

    private val navControllerFlow = MutableStateFlow<NavController?>(null)

    private val navResultMap: HashMap<Int, CompletableFuture<*>> = HashMap()

    suspend fun handleNavigationCommands(navController: NavController) {
        navigationCommands
            .onSubscription { navControllerFlow.value = navController }
            .onCompletion { navControllerFlow.value = null }
            .collect { navController.handleComposeNavigationCommand(it) }
    }

    fun navigate(
        route: String,
        optionsBuilder: (NavOptionsBuilder.() -> Unit)? = null,
        key: Int? = null,
    ) {
        if (key != null) {
            val future = CompletableFuture<Any>()
            navResultMap[key] = future
        }

        navigationCommands.tryEmit(
            NavigationCommand.NavigateToRoute(
                route = route,
            )
        )
    }

    fun navigateForResult() {

    }

    fun navigateUp() {
        navigationCommands.tryEmit(NavigationCommand.NavigateUp)
    }

    private fun NavController.handleComposeNavigationCommand(navigationCommand: NavigationCommand) {
        when (navigationCommand) {
            is NavigationCommand.NavigateToRoute -> {
                navigate(navigationCommand.route, navigationCommand.options)
            }

            NavigationCommand.NavigateUp -> {
                navigateUp()
            }

            is NavigationCommand.PopUpToRoute -> popBackStack(
                navigationCommand.route,
                navigationCommand.inclusive
            )

            is NavigationCommand.NavigateUpWithResult<*> -> {
                navUpWithResult(navigationCommand)
            }

            NavigationCommand.PopAll -> {

            }

            NavigationCommand.PopBackStack -> {
                popBackStack()
            }
        }
    }

    private fun NavController.navUpWithResult(
        navigationCommand: NavigationCommand.NavigateUpWithResult<*>
    ) {
        val backStackEntry =
            navigationCommand.route?.let { getBackStackEntry(it) }
                ?: previousBackStackEntry
        backStackEntry?.savedStateHandle?.set(
            navigationCommand.key,
            navigationCommand.result
        )

        navigationCommand.route?.let {
            popBackStack(it, false)
        } ?: run {
            navigateUp()
        }
    }
}

private fun <T, R> T.navigate(
    route: String,
    optionsBuilder: (NavOptionsBuilder.() -> Unit)? = null,
) {
    LJGNavigator.navigate(
        route = route,
        optionsBuilder = optionsBuilder,
        key = this.hashCode(),
    )
}