package com.github.nullptroma.wallenc.ui.screens.main

import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
import androidx.lifecycle.viewmodel.compose.saveable
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator
import com.github.nullptroma.wallenc.domain.tasks.PipelineState
import com.github.nullptroma.wallenc.domain.tasks.TaskForegroundUiState
import com.github.nullptroma.wallenc.domain.tasks.TaskProgress
import com.github.nullptroma.wallenc.domain.tasks.TaskRunState
import com.github.nullptroma.wallenc.ui.R
import com.github.nullptroma.wallenc.ui.ViewModelBase
import com.github.nullptroma.wallenc.ui.resources.UiStringResolver
import com.github.nullptroma.wallenc.ui.resources.resolve
import com.github.nullptroma.wallenc.ui.screens.ScreenRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsRoute
import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.LocalVaultRoute
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import javax.inject.Inject

@OptIn(ExperimentalCoroutinesApi::class)
@HiltViewModel
class MainViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val taskOrchestrator: ITaskOrchestrator,
    private val vaultsManager: IVaultsManager,
    private val uiStrings: UiStringResolver,
) : ViewModelBase<MainScreenState>(MainScreenState()) {

    @OptIn(SavedStateHandleSaveableApi::class)
    var routes by savedStateHandle.saveable {
        mutableStateOf(
            mapOf<String, ScreenRoute>(
                LocalVaultRoute::class.qualifiedName!! to LocalVaultRoute(),
                RemoteVaultsRoute::class.qualifiedName!! to RemoteVaultsRoute(),
            ),
        )
    }
        private set

    init {
        viewModelScope.launch {
            combine(
                taskOrchestrator.foregroundUi,
                taskOrchestrator.pipelineState,
                vaultsManager.vaults.flatMapLatest { vaults ->
                    if (vaults.isEmpty()) {
                        flowOf(false)
                    } else {
                        combine(vaults.map { it.storagesScanInProgress }) { flags ->
                            flags.any { it }
                        }
                    }
                },
            ) { fg, pipe, anyVaultScanning ->
                mapWorkStatus(fg, pipe, anyVaultScanning)
            }
                .distinctUntilChanged()
                .collect { status ->
                    updateState(state.value.copy(workStatus = status))
                }
        }
    }

    private fun mapWorkStatus(
        fg: TaskForegroundUiState,
        pipe: PipelineState,
        anyVaultScanning: Boolean,
    ): MainWorkStatus {
        when (fg) {
            is TaskForegroundUiState.Visible -> {
                if (fg.tasks.isEmpty()) {
                    return mapBackgroundWork(pipe, anyVaultScanning)
                }
                val head = fg.tasks.first()
                return activeWorkStatusFromProgress(head.title, head.progress)
            }
            TaskForegroundUiState.Hidden -> return mapBackgroundWork(pipe, anyVaultScanning)
        }
    }

    private fun mapBackgroundWork(
        pipe: PipelineState,
        anyVaultScanning: Boolean,
    ): MainWorkStatus {
        val fromPipeline = mapPipelineRunningOnly(pipe)
        if (fromPipeline != null) return fromPipeline
        if (anyVaultScanning) {
            return MainWorkStatus.Active(
                line = uiStrings(R.string.main_status_vault_scanning_storages),
                progressFraction = null,
                indeterminate = true,
            )
        }
        return MainWorkStatus.Idle
    }

    private fun mapPipelineRunningOnly(pipe: PipelineState): MainWorkStatus? {
        val running = pipe.tasks.filter { it.id in pipe.runningTaskIds }
        if (running.isEmpty()) return null
        if (running.size == 1) {
            val t = running.first()
            val progress = (t.state as? TaskRunState.Running)?.progress
            return activeWorkStatusFromProgress(t.title, progress)
        }
        return MainWorkStatus.Active(
            line = uiStrings(R.string.main_status_multiple_tasks, running.size),
            progressFraction = null,
            indeterminate = true,
        )
    }

    private fun activeWorkStatusFromProgress(title: String, progress: TaskProgress?): MainWorkStatus.Active {
        val frac = progress?.fraction
        val indeterminate = progress == null || frac == null
        val label = progress?.label?.resolve(uiStrings)
        val line = if (label != null) "$title$TITLE_LABEL_SEPARATOR$label" else title
        return MainWorkStatus.Active(
            line = line,
            progressFraction = frac,
            indeterminate = indeterminate,
        )
    }

    private companion object {
        private const val TITLE_LABEL_SEPARATOR = " — "
    }
}
