package com.correx.core.tools

import com.correx.core.approvals.Tier
import com.correx.core.events.events.EventMetadata
import com.correx.core.events.events.EventPayload
import com.correx.core.events.events.FileWrittenEvent
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.events.ToolInvocationRequestedEvent
import com.correx.core.events.events.ToolRequest
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.ToolInvocationId
import com.correx.core.tools.state.ToolState
import kotlinx.datetime.Clock
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class FileMutationReducerTest {

    private val reducer = DefaultToolReducer()
    private val inv = ToolInvocationId("inv-1")
    private val session = SessionId("s-1")
    private val stage = StageId("st-1")

    private fun stored(payload: EventPayload, seq: Long) = StoredEvent(
        metadata = EventMetadata(
            eventId = EventId("e-$seq"),
            sessionId = session,
            timestamp = Clock.System.now(),
            schemaVersion = 1,
            causationId = null,
            correlationId = null,
        ),
        sequence = seq,
        sessionSequence = seq,
        payload = payload,
    )

    private val requested = ToolInvocationRequestedEvent(
        invocationId = inv,
        sessionId = session,
        stageId = stage,
        toolName = "file_write",
        tier = Tier.T2,
        request = ToolRequest(inv, session, stage, "file_write", mapOf("path" to "src/A.kt")),
    )

    @Test
    fun `file mutations accumulate onto the matching invocation record`() {
        var state = ToolState()
        state = reducer.reduce(state, stored(requested, 0))
        state = reducer.reduce(
            state,
            stored(
                FileWrittenEvent(
                    invocationId = inv, sessionId = session, path = "src/A.kt",
                    preImageHash = "old", postImageHash = "new", preExisted = true, mode = "0644", timestampMs = 1L,
                ),
                1,
            ),
        )
        state = reducer.reduce(
            state,
            stored(
                FileWrittenEvent(
                    invocationId = inv, sessionId = session, path = "src/B.kt",
                    preImageHash = null, postImageHash = "fresh", preExisted = false, mode = "0644", timestampMs = 2L,
                ),
                2,
            ),
        )

        val mutations = state.invocations.single().mutations
        assertEquals(2, mutations.size)
        assertEquals("src/A.kt", mutations[0].path)
        assertEquals("old", mutations[0].preImageHash)
        assertTrue(mutations[0].preExisted)
        assertEquals("src/B.kt", mutations[1].path)
        assertEquals(null, mutations[1].preImageHash)
        assertEquals(false, mutations[1].preExisted)
    }

    @Test
    fun `file mutation for unknown invocation is a no-op`() {
        val state = reducer.reduce(
            ToolState(),
            stored(
                FileWrittenEvent(
                    invocationId = inv, sessionId = session, path = "x", preExisted = false, timestampMs = 0L,
                ),
                0,
            ),
        )
        assertEquals(0, state.invocations.size)
    }
}
