package com.correx.core.events.events

import com.correx.core.events.types.SessionId
import com.correx.core.events.types.StageId
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/** A critic's verdict for one review iteration. */
@Serializable
enum class CritiqueVerdict { APPROVED, CHANGES_REQUESTED }

/**
 * The findings a critic (plan critic or code reviewer) raised in one review iteration, plus its
 * [verdict] for that iteration. This is the **producing side** of critique calibration: the
 * reviewer-loop runtime correlates the sequence of these across iterations into
 * [CritiqueOutcomeCorrelatedEvent]s (see CritiqueOutcomeCorrelator), which the
 * `:core:critique` projection then folds into per-(model, role) precision.
 *
 * [iteration] is the refinement-loop iteration (1-based) this review belongs to, so the
 * correlator can order reviews and tell whether a finding was fixed between rounds. [modelHash]
 * identifies the model that produced the findings (carried through to the outcome event so
 * calibration is per-model).
 */
@Serializable
@SerialName("CritiqueFindingsRecorded")
data class CritiqueFindingsRecordedEvent(
    val sessionId: SessionId,
    val stageId: StageId,
    val role: CritiqueRole,
    val modelHash: String,
    val iteration: Int,
    val verdict: CritiqueVerdict,
    val findings: List<CritiqueFinding>,
) : EventPayload

/**
 * How a prior [CritiqueFinding] actually turned out once the surrounding loop resolved.
 * UPHELD = the finding was confirmed/actioned; DISMISSED = rejected as a false positive;
 * INCONCLUSIVE = no signal either way.
 */
@Serializable
enum class CritiqueOutcome { UPHELD, DISMISSED, INCONCLUSIVE }

/**
 * Correlates one prior [CritiqueFinding] (by [findingId]) with how it actually turned out, so a
 * critic's precision can be tracked per model and per role over time (the calibration projection
 * in `:core:critique`). [modelHash] identifies the model that produced the finding; [severity]
 * is carried so calibration can be sliced by severity band without re-reading the finding.
 *
 * This is the recording half only — who decides UPHELD/DISMISSED lives in the reviewer-loop
 * runtime and is wired separately.
 */
@Serializable
@SerialName("CritiqueOutcomeCorrelated")
data class CritiqueOutcomeCorrelatedEvent(
    val sessionId: SessionId,
    val findingId: String,
    val role: CritiqueRole,
    val modelHash: String,
    val severity: CritiqueSeverity,
    val outcome: CritiqueOutcome,
) : EventPayload
