package link.socket.ampere.agents.events

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.datetime.Clock
import kotlinx.serialization.json.Json
import link.socket.ampere.agents.domain.Urgency
import link.socket.ampere.agents.domain.cognition.sparks.CognitivePhase
import link.socket.ampere.agents.domain.event.CognitiveEvent
import link.socket.ampere.agents.domain.event.CognitivePhaseEvent
import link.socket.ampere.agents.domain.event.Event
import link.socket.ampere.agents.domain.event.EventSource
import link.socket.ampere.agents.domain.event.MemoryEvent
import link.socket.ampere.agents.domain.event.MilestoneCategory
import link.socket.ampere.agents.domain.event.PermissionDeniedEvent
import link.socket.ampere.agents.domain.event.PermissionDeniedReason
import link.socket.ampere.plugin.permission.PluginPermission

class EventSerializationTest {

    private val json = Json {
        prettyPrint = false
        encodeDefaults = true
        classDiscriminator = "type"
    }

    private val stubTimestamp = Clock.System.now()
    private val stubEventSource = EventSource.Agent("agent-X")

    @Test
    fun `serialize and deserialize task created event as concrete`() {
        val original = Event.TaskCreated(
            eventId = "11111111-1111-1111-1111-111111111111",
            urgency = Urgency.LOW,
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            taskId = "T-001",
            description = "Implement event bus",
            assignedTo = "agent-B",
        )
        val text = json.encodeToString(original)
        val decoded = json.decodeFromString<Event.TaskCreated>(text)
        assertIs<Event.TaskCreated>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize question raised event polymorphic`() {
        val original: Event = Event.QuestionRaised(
            eventId = "22222222-2222-2222-2222-222222222222",
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            questionText = "What database should we use?",
            context = "KMP persistence options",
            urgency = Urgency.HIGH,
        )
        val text = json.encodeToString(Event.serializer(), original)
        val decoded = json.decodeFromString(Event.serializer(), text)
        assertIs<Event.QuestionRaised>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize code submitted event as concrete`() {
        val original = Event.CodeSubmitted(
            eventId = "33333333-3333-3333-3333-333333333333",
            urgency = Urgency.MEDIUM,
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            filePath = "shared/src/commonMain/kotlin/EventBus.kt",
            changeDescription = "Initial commit",
            reviewRequired = true,
            assignedTo = null,
        )
        val text = json.encodeToString(original)
        val decoded = json.decodeFromString<Event.CodeSubmitted>(text)
        assertIs<Event.CodeSubmitted>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize permission denied event polymorphic`() {
        val original: Event = PermissionDeniedEvent(
            eventId = "44444444-4444-4444-4444-444444444444",
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            urgency = Urgency.HIGH,
            pluginId = "example-plugin",
            toolId = "fetch-example",
            toolName = "Fetch Example",
            permission = PluginPermission.NetworkDomain("api.example.com"),
            reason = PermissionDeniedReason.MISSING_GRANT,
        )

        val text = json.encodeToString(Event.serializer(), original)
        val decoded = json.decodeFromString(Event.serializer(), text)

        assertIs<PermissionDeniedEvent>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize phase entered event polymorphic`() {
        val original: Event = CognitivePhaseEvent.PhaseEntered(
            eventId = "55555555-5555-5555-5555-555555555555",
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            agentId = "agent-X",
            oldPhase = null,
            newPhase = CognitivePhase.PERCEIVE,
            nestingDepth = 0,
        )

        val text = json.encodeToString(Event.serializer(), original)
        val decoded = json.decodeFromString(Event.serializer(), text)

        assertIs<CognitivePhaseEvent.PhaseEntered>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize milestone reached event polymorphic`() {
        val original: Event = MemoryEvent.MilestoneReached(
            eventId = "77777777-7777-7777-7777-777777777777",
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            agentId = "agent-X",
            milestoneId = "milestone-1",
            description = "Recovered after a failed retry",
            knowledgeId = "knowledge-1",
            taskId = "task-1",
            runId = "run-1",
            category = MilestoneCategory.RECOVERY,
            urgency = Urgency.MEDIUM,
        )

        val text = json.encodeToString(Event.serializer(), original)
        val decoded = json.decodeFromString(Event.serializer(), text)

        assertIs<MemoryEvent.MilestoneReached>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize escalation fired event polymorphic`() {
        val original: Event = CognitiveEvent.EscalationFired(
            eventId = "66666666-6666-6666-6666-666666666666",
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            urgency = Urgency.HIGH,
            agentId = "agent-X",
            uncertaintyValue = 0.82,
            threshold = 0.7,
            prompt = "Which migration path should we use?",
            cognitivePhase = CognitivePhase.PLAN,
        )

        val text = json.encodeToString(Event.serializer(), original)
        val decoded = json.decodeFromString(Event.serializer(), text)

        assertIs<CognitiveEvent.EscalationFired>(decoded)
        assertEquals(original, decoded)
    }

    @Test
    fun `serialize and deserialize escalation considered event polymorphic`() {
        val original: Event = CognitiveEvent.EscalationConsidered(
            eventId = "66666666-6666-6666-6666-666666666666",
            timestamp = stubTimestamp,
            eventSource = stubEventSource,
            agentId = "agent-X",
            uncertaintyValue = 0.42,
            threshold = 0.7,
            fired = false,
            cognitivePhase = CognitivePhase.PERCEIVE,
        )

        val text = json.encodeToString(Event.serializer(), original)
        val decoded = json.decodeFromString(Event.serializer(), text)

        assertIs<CognitiveEvent.EscalationConsidered>(decoded)
        assertEquals(original, decoded)
        assertEquals(Urgency.LOW, decoded.urgency)
    }
}
