package com.pixelized.desktop.lwa.ui.screen.admin.map.edit

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.network.LwaNetworkException
import com.pixelized.desktop.lwa.repository.map.MapRepository
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldFlow
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.GMMapEditDestination
import com.pixelized.shared.lwa.protocol.rest.APIResponse.ErrorCode
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.runBlocking

class GMMapEditViewModel(
    private val mapRepository: MapRepository,
    private val factory: GMMapEditFactory,
    savedStateHandle: SavedStateHandle,
) : ViewModel() {
    private val argument = GMMapEditDestination.Argument(savedStateHandle)

    private val _error = MutableSharedFlow<ErrorSnackUio>()
    val error: SharedFlow<ErrorSnackUio> get() = _error

    private val _form: StateFlow<GMMapEditPageForm> = mapRepository.mapsFlow()
        .map { factory.createForm(item = it[argument.id]) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily,
            initialValue = runBlocking { factory.createForm(item = null) },
        )
    val form: StateFlow<GMMapEditPageUio> = _form
        .map { factory.createPage(id = argument.id, form = it) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.Lazily,
            initialValue = runBlocking { factory.createPage(id = argument.id, form = _form.value) },
        )

    suspend fun save(
        form: GMMapEditPageUio = this.form.value,
    ): Boolean {
        if (!isFormValid()) return false

        val edited = factory.createItem(form = form) ?: return false

        try {
            mapRepository.updateMap(
                map = edited,
                create = argument.id == null,
            )
            return true
        } catch (exception: LwaNetworkException) {
            _form.value.id.errorFlow.value = exception.code == ErrorCode.MapId
            val message = ErrorSnackUio.from(exception = exception)
            _error.emit(message)
            return false
        } catch (exception: Exception) {
            val message = ErrorSnackUio.from(exception = exception)
            _error.emit(message)
            return false
        }
    }

    private suspend fun isFormValid(): Boolean {
        var isValid = true

        // check for empty values
        _form.value.id.let { field ->
            if (field.valueFlow.value.isBlank()) {
                field.errorFlow.value = false
                isValid = false
            }
        }

        return isValid
    }

    data class GMMapEditPageForm(
        val id: LwaTextFieldFlow,
        val label: LwaTextFieldFlow,
        val cameraZoom: LwaTextFieldFlow,
        val cameraOffsetX: LwaTextFieldFlow,
        val cameraOffsetY: LwaTextFieldFlow,
        val sizeWidth: LwaTextFieldFlow,
        val sizeHeight: LwaTextFieldFlow,
        val rootId: LwaTextFieldFlow,
        val rootName: LwaTextFieldFlow,
        val rootUri: LwaTextFieldFlow,
        val layerId: LwaTextFieldFlow,
        val layerName: LwaTextFieldFlow,
        val layerUri: LwaTextFieldFlow,
    )
}