package com.correx.core.events.events

import com.correx.core.events.types.ProjectId
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.serialization.SerialName
import kotlinx.serialization.Serializable

/**
 * Marker over every task-lifecycle event. Deliberately *not* part of the serialized
 * [EventPayload] hierarchy — concrete events implement both interfaces — so projections
 * can recover the owning [taskId] from any payload via `payload as? TaskEvent` without a
 * giant `when`. Status is never carried on the wire: it is derived by the task reducer
 * from these facts (mirroring how session status is derived from stage events).
 */
sealed interface TaskEvent {
    val taskId: TaskId
}

@Serializable
@SerialName("TaskCreated")
data class TaskCreatedEvent(
    override val taskId: TaskId,
    val projectId: ProjectId,
    val key: String,
    val title: String,
    val goal: String,
    val acceptanceCriteria: List<String> = emptyList(),
    val affectedPaths: List<String> = emptyList(),
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskClaimed")
data class TaskClaimedEvent(
    override val taskId: TaskId,
    val claimant: String,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskReleased")
data class TaskReleasedEvent(
    override val taskId: TaskId,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskBlocked")
data class TaskBlockedEvent(
    override val taskId: TaskId,
    val reason: String,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskUnblocked")
data class TaskUnblockedEvent(
    override val taskId: TaskId,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskSubmittedForReview")
data class TaskSubmittedForReviewEvent(
    override val taskId: TaskId,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskCompleted")
data class TaskCompletedEvent(
    override val taskId: TaskId,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskReopened")
data class TaskReopenedEvent(
    override val taskId: TaskId,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskCancelled")
data class TaskCancelledEvent(
    override val taskId: TaskId,
    val reason: String? = null,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskEdited")
data class TaskEditedEvent(
    override val taskId: TaskId,
    val title: String? = null,
    val goal: String? = null,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskAcceptanceCriteriaSet")
data class TaskAcceptanceCriteriaSetEvent(
    override val taskId: TaskId,
    val criteria: List<String>,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskAffectedPathsSet")
data class TaskAffectedPathsSetEvent(
    override val taskId: TaskId,
    val paths: List<String>,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskLinked")
data class TaskLinkedEvent(
    override val taskId: TaskId,
    val targetId: String,
    // `linkType`, not `type`: the JSON polymorphic class discriminator is "type".
    @SerialName("linkType") val type: TaskLinkType,
    val targetKind: TaskTargetKind,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskUnlinked")
data class TaskUnlinkedEvent(
    override val taskId: TaskId,
    val targetId: String,
    @SerialName("linkType") val type: TaskLinkType,
) : EventPayload, TaskEvent

@Serializable
@SerialName("TaskNoteAdded")
data class TaskNoteAddedEvent(
    override val taskId: TaskId,
    val author: TaskNoteAuthor,
    val body: String,
) : EventPayload, TaskEvent

/**
 * Soft-delete tombstone. The event stays in the log (history is never rewritten); the board
 * projection drops the task from active views. Distinct from cancellation, which is a
 * lifecycle outcome that stays visible as CANCELLED.
 */
@Serializable
@SerialName("TaskDeleted")
data class TaskDeletedEvent(
    override val taskId: TaskId,
) : EventPayload, TaskEvent
