package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.network

import androidx.compose.material.SnackbarDuration
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.repository.settings.model.Settings
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import kotlinx.coroutines.CancellationException
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.combine
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.network__message__action
import lwacharactersheet.composeapp.generated.resources.network__require__player_name
import org.jetbrains.compose.resources.getString

class NetworkViewModel(
    private val settingsRepository: SettingsRepository,
    private val networkRepository: NetworkRepository,
    private val factory: NetworkFactory,
) : ViewModel() {
    private val settings = settingsRepository.settings()
    private val nameFlow = MutableStateFlow(settings.playerName)
    private val hostFlow = MutableStateFlow(settings.network.host)
    private val portFlow = MutableStateFlow(settings.network.port)

    private val _networkError = MutableSharedFlow<ErrorSnackUio>()
    val networkError: SharedFlow<ErrorSnackUio> get() = _networkError

    private val _message = MutableSharedFlow<String>()
    val message: SharedFlow<String> get() = _message

    private val _isLoading = mutableStateOf(false)
    val isLoading: State<Boolean> get() = _isLoading

    val blurController: BlurContentController = BlurContentController()

    val network: StateFlow<NetworkPageUio> = combine(
        settingsRepository.settingsFlow(),
        networkRepository.status,
        nameFlow,
        hostFlow,
        portFlow,
    ) { settings, status, name, host, port ->
        factory.convertToUio(
            player = name,
            status = status,
            host = host,
            resetHost = settings.network.host != host,
            port = port,
            resetPort = settings.network.port != port,
        )
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.Eagerly,
        initialValue = NetworkPageUio.empty()
    )

    init {
        settingsRepository.settingsFlow()
            .onEach {
                nameFlow.value = it.playerName
                hostFlow.value = it.network.host
                portFlow.value = it.network.port
            }
            .launchIn(viewModelScope)
    }

    fun onPlayerNameChange(player: String) {
        nameFlow.value = player
    }

    fun onPortChange(port: String) {
        portFlow.value = port.toIntOrNull() ?: 0
    }

    fun onResetPortChange() {
        portFlow.value = settings.network.port
    }

    fun onHostChange(host: String) {
        hostFlow.value = host
    }

    fun onResetHostChange() {
        hostFlow.value = settings.network.host
    }

    suspend fun connect() {
        blurController.show()
        _isLoading.value = true

        if (
            settings.playerName != nameFlow.value ||
            settings.network.host != hostFlow.value ||
            settings.network.port != portFlow.value
        ) {
            settingsRepository.update(
                settings = settings.copy(
                    playerName = nameFlow.value,
                    network = Settings.Network(
                        host = hostFlow.value,
                        port = portFlow.value,
                    ),
                )
            )
        }

        if (nameFlow.value.isBlank()) {
            _networkError.emit(
                ErrorSnackUio(
                    message = getString(Res.string.network__require__player_name),
                    action = getString(Res.string.network__message__action),
                    duration = SnackbarDuration.Short,
                )
            )
            blurController.hide()
            _isLoading.value = false
            return
        }

        networkRepository.connect(
            host = hostFlow.value,
            port = portFlow.value,
            onConnect = {
                _isLoading.value = false
                blurController.hide()
            },
            onFailure = {
                _isLoading.value = false
                blurController.hide()

                if (it !is CancellationException) {
                    viewModelScope.launch {
                        _networkError.emit(ErrorSnackUio.from(it))
                    }
                }
            },
        )
    }

    fun disconnect() {
        networkRepository.disconnect()
    }
}