package com.correx.apps.server.health

import com.correx.core.events.stores.EventStore
import com.correx.core.sessions.projections.replay.DefaultEventReplayer
import kotlinx.datetime.Clock
import kotlinx.serialization.Serializable

@Serializable
data class HealthSubjectReport(
    val subject: String,
    val status: String,
    val metric: String,
    val observedValue: Long,
    val detail: String,
    val since: String,
)

/**
 * Render-friendly snapshot of system health. [overall] is DEGRADED if any subject is degraded,
 * else HEALTHY (including the "nothing recorded yet" case). [checkedAt] is when this report was
 * rendered, distinct from each subject's [HealthSubjectReport.since] edge timestamp.
 */
@Serializable
data class HealthReport(
    val overall: String,
    val subjects: List<HealthSubjectReport>,
    val checkedAt: String,
)

/**
 * Rebuilds [HealthState] by replaying the system session through [HealthProjection], then renders
 * a [HealthReport]. Replay-only: no probing, no network (Hard Invariant #8). Backs `GET /health/checks`
 * and the `correx health` CLI, the same way [com.correx.apps.server.metrics.MetricsInspectionService]
 * backs `correx stats`.
 */
class HealthInspectionService(
    eventStore: EventStore,
    private val clock: Clock = Clock.System,
) {
    private val replayer = DefaultEventReplayer(eventStore, HealthProjection())

    fun inspect(): HealthReport {
        val state = replayer.rebuild(SYSTEM_SESSION)
        val subjects = state.subjects.entries
            .sortedBy { it.key.name }
            .map { (subject, health) ->
                HealthSubjectReport(
                    subject = subject.name,
                    status = health.status.name,
                    metric = health.metric,
                    observedValue = health.observedValue,
                    detail = health.detail,
                    since = health.since.toString(),
                )
            }
        val overall = if (subjects.any { it.status == HealthStatus.DEGRADED.name }) {
            HealthStatus.DEGRADED.name
        } else {
            HealthStatus.HEALTHY.name
        }
        return HealthReport(overall = overall, subjects = subjects, checkedAt = clock.now().toString())
    }
}
