package com.correx.infrastructure.inference

import com.correx.core.events.types.ProviderId
import com.correx.core.inference.CapabilityScore
import com.correx.core.inference.InferenceProvider
import com.correx.core.inference.InferenceRequest
import com.correx.core.inference.InferenceResponse
import com.correx.core.inference.ModelCapability
import com.correx.core.inference.NoEligibleProviderException
import com.correx.core.inference.ProviderHealth
import com.correx.core.inference.Token
import com.correx.core.inference.Tokenizer
import com.correx.core.utils.TypeId
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class FirstAvailableRoutingStrategyTest {

    private val strategy = FirstAvailableRoutingStrategy()

    private fun fakeProvider(id: String, vararg caps: ModelCapability): InferenceProvider =
        object : InferenceProvider {
            override val id: ProviderId = TypeId(id)
            override val name: String = id
            override val tokenizer: Tokenizer = object : Tokenizer {
                override suspend fun tokenize(text: String): List<Token> = emptyList()
                override suspend fun countTokens(text: String): Int = 0
            }
            override suspend fun infer(request: InferenceRequest): InferenceResponse =
                error("not implemented in test")
            override suspend fun healthCheck(): ProviderHealth = ProviderHealth.Healthy
            override fun capabilities(): Set<CapabilityScore> =
                caps.map { CapabilityScore(it, 1.0) }.toSet()
        }

    @Test
    fun `selects first provider satisfying all required capabilities`() {
        val coding = fakeProvider("codingOnly", ModelCapability.Coding)
        val general = fakeProvider("generalAndCoding", ModelCapability.General, ModelCapability.Coding)
        val required = setOf(ModelCapability.Coding, ModelCapability.General)

        val selected = strategy.select(listOf(coding, general), required)
        assertEquals("generalAndCoding", selected.id.value)
    }

    @Test
    fun `selects first matching provider when multiple satisfy requirements`() {
        val p1 = fakeProvider("p1", ModelCapability.Coding)
        val p2 = fakeProvider("p2", ModelCapability.Coding)

        val selected = strategy.select(listOf(p1, p2), setOf(ModelCapability.Coding))
        assertEquals("p1", selected.id.value)
    }

    @Test
    fun `throws NoEligibleProviderException when no candidate satisfies required capabilities`() {
        val general = fakeProvider("general", ModelCapability.General)
        assertThrows<NoEligibleProviderException> {
            strategy.select(listOf(general), setOf(ModelCapability.Coding))
        }
    }

    @Test
    fun `throws NoEligibleProviderException when candidates list is empty`() {
        assertThrows<NoEligibleProviderException> {
            strategy.select(emptyList(), setOf(ModelCapability.Coding))
        }
    }

    @Test
    fun `selects any provider when required capabilities is empty`() {
        val p = fakeProvider("p", ModelCapability.General)
        val selected = strategy.select(listOf(p), emptySet())
        assertEquals("p", selected.id.value)
    }
}
