package com.flagship.abox.manager.testing.fakes

import com.flagship.abox.manager.domain.DeviceCatalog
import com.flagship.abox.manager.domain.DeviceDescriptor
import com.flagship.abox.manager.domain.DeviceId
import com.flagship.abox.manager.domain.DeviceName
import com.flagship.abox.manager.domain.DeviceType
import com.flagship.abox.manager.domain.DomainError
import com.flagship.abox.manager.domain.DomainResult
import com.flagship.abox.manager.domain.ProviderProfileId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import java.util.concurrent.ConcurrentHashMap

class FakeDeviceCatalog : DeviceCatalog {
    private val devices = ConcurrentHashMap<
        ProviderProfileId,
        MutableStateFlow<DomainResult<List<DeviceDescriptor>>>,
    >()

    override fun observeDevices(
        profileId: ProviderProfileId,
    ): Flow<DomainResult<List<DeviceDescriptor>>> = deviceFlow(profileId)

    fun setDevices(profileId: ProviderProfileId, value: List<DeviceDescriptor>) {
        require(value.all { it.profileId == profileId }) {
            "All devices must belong to the supplied provider profile"
        }
        deviceFlow(profileId).value = DomainResult.Success(value)
    }

    fun setError(profileId: ProviderProfileId, error: DomainError) {
        deviceFlow(profileId).value = DomainResult.Failure(error)
    }

    private fun deviceFlow(
        profileId: ProviderProfileId,
    ): MutableStateFlow<DomainResult<List<DeviceDescriptor>>> = devices.getOrPut(profileId) {
        MutableStateFlow(DomainResult.Success(defaultDevices(profileId)))
    }

    private fun defaultDevices(profileId: ProviderProfileId): List<DeviceDescriptor> = listOf(
        DeviceDescriptor(
            id = DeviceId("${profileId.value}:socket-1"),
            profileId = profileId,
            name = DeviceName("socket-1"),
            type = DeviceType.SOCKET,
        ),
        DeviceDescriptor(
            id = DeviceId("${profileId.value}:th-1"),
            profileId = profileId,
            name = DeviceName("th-1"),
            type = DeviceType.TEMPERATURE_HUMIDITY,
        ),
        DeviceDescriptor(
            id = DeviceId("${profileId.value}:ir-1"),
            profileId = profileId,
            name = DeviceName("ir-1"),
            type = DeviceType.INFRARED,
        ),
    )
}
