package com.correx.apps.server.concept

import com.correx.core.events.events.ConceptPromotedEvent
import com.correx.core.events.events.EventMetadata
import com.correx.core.events.events.EventPayload
import com.correx.core.events.events.NewEvent
import com.correx.core.events.events.RetryAttemptedEvent
import com.correx.core.events.events.StageCompletedEvent
import com.correx.core.events.stores.EventStore
import com.correx.core.events.types.EventId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import com.correx.core.events.types.TransitionId
import com.correx.core.inference.Embedder
import com.correx.core.talkie.l3.InMemoryL3MemoryStore
import com.correx.core.talkie.l3.L3Query
import com.correx.infrastructure.persistence.InMemoryEventStore
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.util.UUID

private class ConstantEmbedder(override val dimension: Int = 8) : Embedder {
    override suspend fun embed(text: String): FloatArray = FloatArray(dimension) { 1f }
}

class ConceptCompilerServiceTest {

    private val store = InMemoryEventStore()
    private val l3 = InMemoryL3MemoryStore()
    private val service = ConceptCompilerService(store, ConstantEmbedder(), l3, threshold = 3)

    private suspend fun append(payload: EventPayload, session: String) {
        store.append(
            NewEvent(
                metadata = EventMetadata(
                    eventId = EventId(UUID.randomUUID().toString()),
                    sessionId = SessionId(session),
                    timestamp = Clock.System.now(),
                    schemaVersion = 1,
                    causationId = null,
                    correlationId = null,
                ),
                payload = payload,
            ),
        )
    }

    private suspend fun validatedFix(fp: String, session: String) {
        append(
            RetryAttemptedEvent(
                sessionId = SessionId(session),
                stageId = StageId("impl"),
                attemptNumber = 1,
                maxAttempts = 3,
                failureReason = "detekt: MagicNumber\ntrace",
                gate = "lint",
                fingerprint = fp,
            ),
            session,
        )
        append(StageCompletedEvent(SessionId(session), StageId("impl"), TransitionId("t")), session)
    }

    private suspend fun promotedEvents(): List<ConceptPromotedEvent> =
        store.allEvents().mapNotNull { it.payload as? ConceptPromotedEvent }.toList()

    @Test
    fun `promotes a fingerprint that crossed the threshold and injects it into L3`(): Unit = runBlocking {
        validatedFix("fp1", "sa")
        validatedFix("fp1", "sb")
        validatedFix("fp1", "sc")

        service.runOnce()

        val promoted = promotedEvents()
        assertEquals(1, promoted.size)
        assertEquals("fp1", promoted.single().fingerprint)
        assertEquals(3, promoted.single().occurrences)

        val hits = l3.query(L3Query(vector = FloatArray(8) { 1f }, k = 5))
        assertTrue(hits.any { it.entry.turnId == "concept:fp1" }) { "concept not injected into L3" }
    }

    @Test
    fun `does not promote below the threshold`(): Unit = runBlocking {
        validatedFix("fp1", "sa")
        validatedFix("fp1", "sb")

        service.runOnce()

        assertTrue(promotedEvents().isEmpty())
    }

    @Test
    fun `repeated runs do not re-promote the same fingerprint`(): Unit = runBlocking {
        validatedFix("fp1", "sa")
        validatedFix("fp1", "sb")
        validatedFix("fp1", "sc")

        service.runOnce()
        service.runOnce()

        assertEquals(1, promotedEvents().size)
    }
}
