package com.correx.core.kernel.orchestration

import com.correx.core.validation.artifact.ArtifactFailure

/**
 * What to do when the artifact-emission repair ladder (deterministic + one LLM rung) still can't
 * produce a schema-valid artifact. Each maps onto machinery the orchestrator already has.
 * See docs/specs/2026-07-04-artifact-emission-pipeline.md §3.4.
 */
sealed interface ArtifactPolicyDecision {
    data object RetryProducer : ArtifactPolicyDecision           // → retryStageOrFail (retryable failure)
    data object RegenerateArtifactOnly : ArtifactPolicyDecision  // → re-run the tools-less emission nudge
    data object Abort : ArtifactPolicyDecision                   // → non-retryable failure
    data object EscalateHuman : ArtifactPolicyDecision           // → approval gate / OrchestrationPaused
}

/**
 * Pure default policy. `attemptsSoFar` is the repair attempts already spent this stage.
 * Mapping (spec §3.4): UNSAFE→Abort; CONTRADICTORY→RetryProducer; MISSING_INFO→RegenerateArtifactOnly
 * first, then RetryProducer; FORMATTING/SCHEMA→RetryProducer (deterministic + LLM rung already tried);
 * exhausted→EscalateHuman when an approver is attached, else Abort.
 */
fun decide(
    failure: ArtifactFailure,
    attemptsSoFar: Int,
    maxAttempts: Int,
    hasApprover: Boolean,
): ArtifactPolicyDecision {
    if (failure == ArtifactFailure.UNSAFE) return ArtifactPolicyDecision.Abort
    val base = when (failure) {
        ArtifactFailure.MISSING_INFO -> if (attemptsSoFar == 0) {
            ArtifactPolicyDecision.RegenerateArtifactOnly
        } else {
            ArtifactPolicyDecision.RetryProducer
        }
        else -> ArtifactPolicyDecision.RetryProducer // FORMATTING, SCHEMA, CONTRADICTORY
    }
    // Exhaustion guard: a retry-flavoured decision with no budget left escalates (with an approver) or aborts.
    return if (attemptsSoFar >= maxAttempts) {
        if (hasApprover) ArtifactPolicyDecision.EscalateHuman else ArtifactPolicyDecision.Abort
    } else {
        base
    }
}
