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

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.github.nullptroma.wallenc.domain.datatypes.TwoFaTokenRecord
import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator
import com.github.nullptroma.wallenc.domain.tasks.TaskProgressLabel
import com.github.nullptroma.wallenc.domain.tasks.VaultTaskStep
import com.github.nullptroma.wallenc.domain.tasks.TaskRunState
import com.github.nullptroma.wallenc.ui.R
import com.github.nullptroma.wallenc.domain.errors.WallencException
import com.github.nullptroma.wallenc.ui.ViewModelBase
import com.github.nullptroma.wallenc.ui.resources.toUserNotification
import com.github.nullptroma.wallenc.ui.resources.UiStringResolver
import com.github.nullptroma.wallenc.ui.screens.main.screens.storage.requireStorageUuid
import com.github.nullptroma.wallenc.usecases.FindStorageUseCase
import com.github.nullptroma.wallenc.usecases.ManageTwoFaTokensUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class TwoFaTokensViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val findStorageUseCase: FindStorageUseCase,
    private val manageTwoFaTokensUseCase: ManageTwoFaTokensUseCase,
    private val taskOrchestrator: ITaskOrchestrator,
    private val uiStrings: UiStringResolver,
) : ViewModelBase<TwoFaTokensScreenState>(TwoFaTokensScreenState()) {

    private val storageUuid = savedStateHandle.requireStorageUuid()

    init {
        observeStorageTokens()
    }

    private fun observeStorageTokens() {
        viewModelScope.launch {
            val storage = findStorageUseCase.find(storageUuid)
            if (storage == null) {
                updateState(
                    state.value.copy(
                        isLoading = false,
                        errorNotification = WallencException.Feature.StorageNotFound().toUserNotification(),
                    ),
                )
                return@launch
            }
            if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) {
                updateState(
                    state.value.copy(
                        isLoading = false,
                        isAvailable = false,
                        errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(),
                    ),
                )
                return@launch
            }
            combine(
                storage.isAvailable,
                manageTwoFaTokensUseCase.observe(storage),
                taskOrchestrator.pipelineState.map { pipe ->
                    pipe.tasks.any { t ->
                        t.busyStorageUuid == storage.uuid && isTaskActive(t.state)
                    }
                },
            ) { available, items, isMutating ->
                state.value.copy(
                    isLoading = false,
                    isAvailable = available,
                    isMutating = isMutating,
                    items = items,
                    errorNotification = null,
                )
            }.collect { ui ->
                updateState(ui)
            }
        }
    }

    fun saveToken(
        existingId: String?,
        issuer: String,
        account: String,
        secret: String,
        notes: String?,
        digits: Int = 6,
        periodSeconds: Int = 30,
        algorithm: String = "SHA1",
    ) {
        viewModelScope.launch {
            val storage = findStorageUseCase.find(storageUuid) ?: return@launch
            if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) {
                updateState(
                    state.value.copy(
                        errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(),
                    ),
                )
                return@launch
            }
            taskOrchestrator.enqueue(
                title = uiStrings(R.string.task_title_save_2fa_token),
                dispatcher = Dispatchers.IO,
                busyStorageUuid = storage.uuid,
                work = { ctx ->
                    ctx.reportProgress(null, TaskProgressLabel.VaultTask(VaultTaskStep.Save2FaToken))
                    if (existingId == null) {
                        manageTwoFaTokensUseCase.create(
                            storageInfo = storage,
                            issuer = issuer,
                            account = account,
                            secret = secret,
                            notes = notes,
                            digits = digits,
                            periodSeconds = periodSeconds,
                            algorithm = algorithm,
                        )
                    } else {
                        manageTwoFaTokensUseCase.update(
                            storageInfo = storage,
                            token = TwoFaTokenRecord(
                                id = existingId,
                                issuer = issuer,
                                account = account,
                                secret = secret,
                                notes = notes,
                                digits = digits,
                                periodSeconds = periodSeconds,
                                algorithm = algorithm,
                            ),
                        )
                    }
                },
            )
        }
    }

    fun deleteToken(id: String) {
        viewModelScope.launch {
            val storage = findStorageUseCase.find(storageUuid) ?: return@launch
            if (storage.metaInfo.value.encInfo != null && !storage.isVirtualStorage) {
                updateState(
                    state.value.copy(
                        errorNotification = WallencException.Feature.NeedsDecryptedView().toUserNotification(),
                    ),
                )
                return@launch
            }
            taskOrchestrator.enqueue(
                title = uiStrings(R.string.task_title_delete_2fa_token),
                dispatcher = Dispatchers.IO,
                busyStorageUuid = storage.uuid,
                work = { ctx ->
                    ctx.reportProgress(null, TaskProgressLabel.VaultTask(VaultTaskStep.Delete2FaToken))
                    manageTwoFaTokensUseCase.delete(storage, id)
                },
            )
        }
    }

    private fun isTaskActive(state: TaskRunState): Boolean =
        state is TaskRunState.Queued || state is TaskRunState.Running
}
