/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.ui.connect

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.runicgateway.app.core.net.ServerUrl
import com.runicgateway.app.data.repository.ConnectionRepository
import com.runicgateway.app.data.repository.ConnectionRepository.ProbeResult
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
 * Drives the first-run connect screen (PLAN.md §3): validates + probes the
 * entered base URL, and on success activates it for the whole app. The error is
 * exposed as a resource-free [ConnectError] the screen maps to localized copy.
 */
@HiltViewModel
class ConnectViewModel @Inject constructor(
    private val connectionRepository: ConnectionRepository,
) : ViewModel() {

    sealed interface ConnectError {
        data object Blank : ConnectError
        data object Malformed : ConnectError
        data object UnsupportedScheme : ConnectError
        data object Insecure : ConnectError
        data object NotRunicGateway : ConnectError
        data class VersionMismatch(val serverApi: String) : ConnectError
        data object Unreachable : ConnectError
        data class Server(val status: Int) : ConnectError
    }

    data class UiState(
        val input: String = "",
        val submitting: Boolean = false,
        val error: ConnectError? = null,
    )

    private val _state = MutableStateFlow(UiState())
    val state: StateFlow<UiState> = _state.asStateFlow()

    fun onInputChange(value: String) {
        _state.update { it.copy(input = value, error = null) }
    }

    fun connect(onSuccess: () -> Unit) {
        if (_state.value.submitting) return
        _state.update { it.copy(submitting = true, error = null) }
        viewModelScope.launch {
            val result = connectionRepository.probeAndConnect(_state.value.input)
            when (result) {
                is ProbeResult.Success -> {
                    _state.update { it.copy(submitting = false) }
                    onSuccess()
                }
                is ProbeResult.InvalidUrl -> fail(result.reason.toError())
                ProbeResult.NotRunicGateway -> fail(ConnectError.NotRunicGateway)
                is ProbeResult.VersionMismatch -> fail(ConnectError.VersionMismatch(result.serverApi))
                is ProbeResult.Unreachable -> fail(ConnectError.Unreachable)
                is ProbeResult.ServerError -> fail(ConnectError.Server(result.status))
            }
        }
    }

    private fun fail(error: ConnectError) {
        _state.update { it.copy(submitting = false, error = error) }
    }

    private fun ServerUrl.Reason.toError(): ConnectError = when (this) {
        ServerUrl.Reason.BLANK -> ConnectError.Blank
        ServerUrl.Reason.MALFORMED -> ConnectError.Malformed
        ServerUrl.Reason.UNSUPPORTED_SCHEME -> ConnectError.UnsupportedScheme
        ServerUrl.Reason.INSECURE -> ConnectError.Insecure
    }
}
