package com.jaytux.simd.frontend.ui

import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import com.jaytux.simd.frontend.Either
import com.jaytux.simd.frontend.client.Client
import com.jaytux.simd.frontend.client.Loader
import com.jaytux.simd.frontend.client.Loader.loadAll
import com.jaytux.simd.frontend.immutable
import com.jaytux.simd.frontend.left
import com.jaytux.simd.frontend.right
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlin.uuid.ExperimentalUuidApi
import kotlin.uuid.Uuid

@OptIn(ExperimentalUuidApi::class)
class DataState(
    val scope: CoroutineScope,
    val addSnackBar: suspend (message: String) -> Unit,
) {
    data class SubState<T>(
        val stateMut: MutableState<String> = mutableStateOf(""),
        val dataMut: MutableState<List<T>> = mutableStateOf(listOf())
    ) {
        val state = stateMut.immutable()
        val data = dataMut.immutable()
    }

    // --- Intrinsic Summaries ---
    private val _summary = SubState<Client.IntrinsicSummary>()
    val summaryState = _summary.state
    val summaries = _summary.data

    // --- CPUIDs ---
    private val _cpuid = SubState<String>()
    val cpuidState = _cpuid.state
    val cpuid = _cpuid.data
    private val _selectedCpuids = mutableStateOf(listOf<Int>())
    val selectedCpuids = _selectedCpuids.immutable()

    // --- Techs ---
    private val _techs = SubState<String>()
    val techsState = _techs.state
    val techs = _techs.data
    private val _selectedTechs = mutableStateOf(listOf<Int>())
    val selectedTechs = _selectedTechs.immutable()

    // --- CppTypes ---
    private val _cppTypes = SubState<String>()
    val cppTypesState = _cppTypes.state
    val cppTypes = _cppTypes.data
    private var filterByType: String? = null

    // --- Categories ---
    private val _categories = SubState<String>()
    val categoriesState = _categories.state
    val categories = _categories.data
    private val _selectedCats = mutableStateOf(listOf<Int>())
    val selectedCats = _selectedCats.immutable()

    // --- Details ---
    private val _intrinsics = mutableMapOf<Uuid, MutableState<Either<String, Client.IntrinsicDetails>?>>()
    private val _intrinsicLoaders = mutableMapOf<Uuid, Job>()

    // --- Version ---
    private val _version = mutableStateOf<Client.Versioning?>(null)
    val version = _version.immutable()

    // --- Filtering ---
    private var _filterJob: Job? = null
    private val _filterData = mutableStateOf(listOf<Client.IntrinsicSummary>())
    val filterData = _filterData.immutable()

    init {
        _filterJob = scope.loadAll(
            client = { Client.getAll() },
            onError = addSnackBar,
            onFinishSuccess = { /*_summary.dataMut.value = it*/ _filterData.value = it },
            onStatusChange = { _summary.stateMut.value = it },
            onProgress = { /*_summary.dataMut.value = it*/ _filterData.value = it }
        )
        scope.loadAll(
            client = { Client.getCpuid() },
            onError = addSnackBar,
            onFinishSuccess = { _cpuid.dataMut.value = it },
            onStatusChange = { _cpuid.stateMut.value = it }
        )
        scope.loadAll(
            client = { Client.getTech() },
            onError = addSnackBar,
            onFinishSuccess = { _techs.dataMut.value = it },
            onStatusChange = { _techs.stateMut.value = it }
        )
        scope.loadAll(
            client = { Client.getCategory() },
            onError = addSnackBar,
            onFinishSuccess = { _categories.dataMut.value = it },
            onStatusChange = { _categories.stateMut.value = it }
        )
        scope.loadAll(
            client = { Client.getTypes() },
            onError = addSnackBar,
            onFinishSuccess = { _cppTypes.dataMut.value = it },
            onStatusChange = { _cppTypes.stateMut.value = it }
        )

        scope.launch {
            Client.getVersion().foldSuspend(addSnackBar) {
                _version.value = it
            }
        }
    }

    operator fun get(uuid: Uuid): State<Either<String, Client.IntrinsicDetails>?> =
        _intrinsics.getOrPut(uuid) { mutableStateOf(null) }

    fun toggleTech(idx: Int) {
        if (idx in _selectedTechs.value) _selectedTechs.value -= idx
        else _selectedTechs.value += idx
    }

    fun toggleCategory(idx: Int) {
        if (idx in _selectedCats.value) _selectedCats.value -= idx
        else _selectedCats.value += idx
    }

    fun toggleCpuid(idx: Int) {
        if (idx in _selectedCpuids.value) _selectedCpuids.value -= idx
        else _selectedCpuids.value += idx
    }

    fun setReturn(type: String) {
        filterByType = if(type !in cppTypes.value) null else type
    }

    fun loadDetails(uuid: Uuid) {
        val current = _intrinsicLoaders[uuid]
        if(current != null && !current.isActive) return

        _intrinsicLoaders[uuid] = scope.launch {
            Client.getDetails(uuid).foldSuspend({
                val msg = "Failed to load details for $uuid: $it"
                addSnackBar(msg)
                _intrinsics.getOrPut(uuid) { mutableStateOf(null) }.value = msg.left()
            }) {
                _intrinsics.getOrPut(uuid) { mutableStateOf(null) }.value = it.right()
            }
        }
    }

    fun doFilter(name: String, desc: String) {
        _filterJob?.cancel()
        _filterJob = scope.loadAll(
            client = {
                Client.getSearch(
                    name = name.ifBlank { null },
                    returnT = filterByType,
                    cpuid = _selectedCpuids.value.map { cpuid.value[it] },
                    tech = _selectedTechs.value.map { techs.value[it] },
                    category = _selectedCats.value.map { categories.value[it] },
                    desc = desc.ifBlank { null }
                )
            },
            onError = addSnackBar,
            onFinishSuccess = { _filterData.value = it },
            onStatusChange = {},
            onProgress = { _filterData.value = it },
            throttleBefore = 250L
        )
    }
}