package com.correx.core.tasks

import com.correx.core.events.events.EventMetadata
import com.correx.core.events.events.EventPayload
import com.correx.core.events.events.StoredEvent
import com.correx.core.events.events.TaskAcceptanceCriteriaSetEvent
import com.correx.core.events.events.TaskBlockedEvent
import com.correx.core.events.events.TaskClaimedEvent
import com.correx.core.events.events.TaskCompletedEvent
import com.correx.core.events.events.TaskCreatedEvent
import com.correx.core.events.events.TaskLinkedEvent
import com.correx.core.events.events.TaskNoteAddedEvent
import com.correx.core.events.events.RefinementIterationEvent
import com.correx.core.events.events.TaskSubmittedForReviewEvent
import com.correx.core.events.events.TaskUnblockedEvent
import com.correx.core.events.types.EventId
import com.correx.core.events.types.ProjectId
import com.correx.core.events.types.SessionId
import com.correx.core.events.types.TaskId
import com.correx.core.events.types.TaskLinkType
import com.correx.core.events.types.TaskNoteAuthor
import com.correx.core.events.types.TaskTargetKind
import kotlinx.datetime.Instant
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test

class DefaultTaskReducerTest {

    private val reducer = DefaultTaskReducer()
    private val taskId = TaskId("auth-1")
    private val projectId = ProjectId("auth")

    private fun stored(payload: EventPayload, seq: Long = 1L) = StoredEvent(
        metadata = EventMetadata(
            eventId = EventId("e-$seq"),
            sessionId = SessionId("tasks:auth"),
            timestamp = Instant.parse("2026-01-0${seq}T00:00:00Z"),
            schemaVersion = 1,
            causationId = null,
            correlationId = null,
        ),
        sequence = seq,
        sessionSequence = seq,
        payload = payload,
    )

    private fun reduceAll(vararg payloads: EventPayload): TaskState =
        payloads.foldIndexed(TaskState()) { i, state, payload ->
            reducer.reduce(state, stored(payload, (i + 1).toLong()))
        }

    private fun created() = TaskCreatedEvent(
        taskId = taskId,
        projectId = projectId,
        key = "auth-1",
        title = "Implement JWT refresh flow",
        goal = "users stay authenticated",
    )

    @Test
    fun `TaskCreatedEvent sets fields and TODO status`() {
        val state = reduceAll(created())

        assertEquals(TaskStatus.TODO, state.status)
        assertEquals("auth-1", state.key)
        assertEquals(projectId, state.projectId)
        assertEquals("Implement JWT refresh flow", state.title)
        assertEquals("users stay authenticated", state.goal)
        assertEquals(Instant.parse("2026-01-01T00:00:00Z"), state.createdAt)
        assertEquals(0, state.invalidTransitions)
    }

    @Test
    fun `claiming moves TODO to IN_PROGRESS and records claimant`() {
        val state = reduceAll(created(), TaskClaimedEvent(taskId, claimant = "claude-opus"))

        assertEquals(TaskStatus.IN_PROGRESS, state.status)
        assertEquals("claude-opus", state.claimant)
    }

    @Test
    fun `happy path created to claimed to review to done`() {
        val state = reduceAll(
            created(),
            TaskClaimedEvent(taskId, claimant = "claude-opus"),
            TaskSubmittedForReviewEvent(taskId),
            TaskCompletedEvent(taskId),
        )

        assertEquals(TaskStatus.DONE, state.status)
        assertEquals(0, state.invalidTransitions)
    }

    @Test
    fun `unblock restores the working status of a claimed task`() {
        val state = reduceAll(
            created(),
            TaskClaimedEvent(taskId, claimant = "claude-opus"),
            TaskBlockedEvent(taskId, reason = "waiting on auth-138"),
            TaskUnblockedEvent(taskId),
        )

        assertEquals(TaskStatus.IN_PROGRESS, state.status)
        assertEquals(0, state.invalidTransitions)
    }

    @Test
    fun `illegal transition is ignored and counted`() {
        val state = reduceAll(created(), TaskCompletedEvent(taskId))

        assertEquals(TaskStatus.TODO, state.status)
        assertEquals(1, state.invalidTransitions)
    }

    @Test
    fun `links and notes accumulate without affecting status`() {
        val state = reduceAll(
            created(),
            TaskAcceptanceCriteriaSetEvent(taskId, listOf("refresh endpoint exists")),
            TaskLinkedEvent(
                taskId,
                targetId = "auth-138",
                type = TaskLinkType.DEPENDS_ON,
                targetKind = TaskTargetKind.TASK,
            ),
            TaskNoteAddedEvent(taskId, author = TaskNoteAuthor.AGENT, body = "migration failed"),
        )

        assertEquals(TaskStatus.TODO, state.status)
        assertEquals(listOf("refresh endpoint exists"), state.acceptanceCriteria)
        assertEquals(1, state.links.size)
        assertEquals(TaskLink("auth-138", TaskLinkType.DEPENDS_ON, TaskTargetKind.TASK), state.links.single())
        assertEquals(1, state.notes.size)
        assertEquals(TaskNoteAuthor.AGENT, state.notes.single().author)
    }

    @Test
    fun `non-task payload leaves state untouched`() {
        val before = reduceAll(created())
        val nonTask = RefinementIterationEvent(
            sessionId = SessionId("s-1"),
            cycleKey = "implement->review",
            iteration = 1,
            maxIterations = 3,
        )
        val after = reducer.reduce(before, stored(nonTask, seq = 9L))

        assertEquals(before, after)
        assertNull(after.claimant)
    }
}
