package com.correx.testing.fixtures.inference

import com.correx.core.events.types.ProviderId
import com.correx.core.inference.CapabilityScore
import com.correx.core.inference.FinishReason
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.ProviderHealth
import com.correx.core.inference.TokenUsage
import com.correx.core.inference.Tokenizer
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive

/**
 * Configurable fake provider for deterministic tests.
 *
 * Modes (mutually exclusive, checked in order):
 *  1. [forcedFailure] — throws [InferenceProviderException] immediately
 *  2. [artificialDelayMs] > 0 — suspends before responding (tests timeout/cancel)
 *  3. default — returns [fixedResponse] immediately
 */
@Suppress("LongParameterList")
class MockInferenceProvider(
    override val id: ProviderId = ProviderId("mock"),
    override val name: String = "Mock Provider",
    private val fixedResponse: String = "mock response",
    private val artificialDelayMs: Long = 0L,
    private val forcedFailure: String? = null,
    private val declaredCapabilities: Set<CapabilityScore> = setOf(
        CapabilityScore(ModelCapability.General, 1.0),
    ),
    private val health: ProviderHealth = ProviderHealth.Healthy,
) : InferenceProvider {

    override val tokenizer: Tokenizer = MockTokenizer()

    var inferCallCount: Int = 0
        private set

    override suspend fun infer(request: InferenceRequest): InferenceResponse {
        inferCallCount++

        if (forcedFailure != null) throw InferenceProviderException(forcedFailure)

        if (artificialDelayMs > 0L) {
            delay(artificialDelayMs)
            currentCoroutineContext().ensureActive()   // cooperative cancellation checkpoint
        }

        val usage = TokenUsage(
            promptTokens = tokenizer.countTokens(request.contextPack.toString()),
            completionTokens = tokenizer.countTokens(fixedResponse),
        )

        return InferenceResponse(
            requestId = request.requestId,
            text = fixedResponse,
            finishReason = FinishReason.Stop,
            tokensUsed = usage,
            latencyMs = artificialDelayMs,
        )
    }

    override suspend fun healthCheck(): ProviderHealth = health

    override fun capabilities(): Set<CapabilityScore> = declaredCapabilities
}

class InferenceProviderException(message: String) : Exception(message)
