package com.pixelized.desktop.lwa.ui.screen.admin.alteration.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.alteration.AlterationRepository
import com.pixelized.desktop.lwa.repository.tag.TagRepository
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.GMAlterationEditDestination
import com.pixelized.desktop.lwa.ui.screen.admin.common.tag.GMTagUio
import com.pixelized.shared.lwa.protocol.rest.APIResponse.ErrorCode
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
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.flow.update
import kotlinx.coroutines.launch

class GMAlterationEditViewModel(
    private val alterationRepository: AlterationRepository,
    private val tagRepository: TagRepository,
    private val factory: GMAlterationEditFactory,
    savedStateHandle: SavedStateHandle,
) : ViewModel() {
    private val argument = GMAlterationEditDestination.Argument(savedStateHandle)

    private val _form = MutableStateFlow<GMAlterationEditForm?>(null)
    val form: StateFlow<GMAlterationEditPageUio?> = _form.map { form ->
        if (form == null) return@map null
        factory.createForm(
            originId = argument.id,
            form = form,
        )
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Lazily,
        initialValue = null,
    )

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

    init {
        viewModelScope.launch {
            _form.value = factory.createForm(
                alteration = alterationRepository.alteration(alterationId = argument.id),
                tags = tagRepository.alterationsTags(),
            )
        }
    }

    suspend fun save(): Boolean {
        val edited = factory.createAlteration(form = _form.value) ?: return false

        try {
            alterationRepository.updateAlteration(
                alteration = edited,
                create = argument.id == null,
            )
            return true
        } catch (exception: LwaNetworkException) {
            _form.value?.idFlow?.errorFlow?.value = exception.code == ErrorCode.AlterationId
            _form.value?.labelFlow?.errorFlow?.value = exception.code == ErrorCode.AlterationName

            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
        }
    }

    suspend fun addField() {
        form.value?.fields?.update { fields ->
            fields.toMutableList().also {
                it.add(element = factory.createField(alteration = null))
            }
        }
    }

    fun removeField(index: Int) {
        form.value?.fields?.update { fields ->
            fields.toMutableList().also {
                it.removeAt(index = index)
            }
        }
    }

    fun addTag(tag: GMTagUio) {
        form.value?.tags?.update { tags ->
            tags.toMutableList().also {
                val index = it.indexOf(tag)
                if (index > -1) {
                    it[index] = tag.copy(highlight = tag.highlight.not())
                }
            }
        }
    }

    data class GMAlterationEditForm(
        val idFlow: LwaTextFieldFlow,
        val labelFlow: LwaTextFieldFlow,
        val descriptionFlow: LwaTextFieldFlow,
        val iconFlow: LwaTextFieldFlow,
        val tagFlow: MutableStateFlow<List<GMTagUio>>,
        val fieldsFlow: MutableStateFlow<List<GMAlterationEditPageUio.SkillUio>>,
    )
}