/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2026 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.kotlin.ui

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlin.reflect.KProperty

private val uiStateScope = CoroutineScope(SupervisorJob() + Dispatchers.Unconfined)

context(render: StateFlowListener)
fun <T> StateFlow<T>.listen(): StateFlow<T> {
    render.track(this) {
        drop(1).onEach { render.update() }.launchIn(uiStateScope)
    }
    return this
}

context(render: StateFlowListener)
fun <T> MutableStateFlow<T>.listen(): MutableStateFlow<T> {
    render.track(this) {
        drop(1).onEach { render.update() }.launchIn(uiStateScope)
    }
    return this
}

fun <T> StateFlow<T>.listen(callback: (T) -> Unit): () -> Unit {
    callback(value)
    val job = drop(1).onEach { callback(it) }.launchIn(uiStateScope)
    return { job.cancel() }
}

operator fun <T> StateFlow<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

operator fun <T> MutableStateFlow<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
    this.value = value
}

fun <T, R> StateFlow<T>.map(mapper: (T) -> R): StateFlow<R> = MappedStateFlow(this, mapper)

@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
private class MappedStateFlow<T, R>(
    private val parent: StateFlow<T>,
    private val mapper: (T) -> R,
): StateFlow<R> {
    override val replayCache: List<R>
        get() = listOf(value)

    override val value: R
        get() = mapper(parent.value)

    override suspend fun collect(collector: FlowCollector<R>): Nothing {
        var initialized = false
        var previous: Any? = null

        parent.collect {
            val mapped = mapper(it)
            if (!initialized || previous != mapped) {
                initialized = true
                previous = mapped
                collector.emit(mapped)
            }
        }
    }
}
