package com.correx.apps.server.metrics

import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable

/**
 * Read-model accumulated by [MetricsProjection] — a pure fold over a single session's event log.
 *
 * Holds raw sums only. Derived ratios (token throughput, average wait, time-accounting
 * percentages) are computed at render time by [MetricsInspectionService] so this stays a
 * stable, replayable accumulator. Never persisted — rebuilt from events on demand
 * (Hard Invariant #1: the event log is the only source of truth).
 */
@Serializable
data class MetricsState(
    val firstEventAt: Instant? = null,
    val lastEventAt: Instant? = null,
    val eventCount: Long = 0,
    val inference: InferenceMetrics = InferenceMetrics(),
    val tools: ToolMetrics = ToolMetrics(),
    val approvals: ApprovalMetrics = ApprovalMetrics(),
    val failures: FailureMetrics = FailureMetrics(),
    val quality: QualityMetrics = QualityMetrics(),
    /**
     * In-flight approval requests awaiting resolution: requestId → request-event timestamp.
     * Transient correlation bookkeeping that lets request→decision latency be computed in a
     * single forward fold. Resolved requests are removed; entries still present after the
     * full replay are unresolved (their wait time is unknown and excluded from totals).
     */
    val pendingApprovals: Map<String, Instant> = emptyMap(),
)

@Serializable
data class InferenceMetrics(
    val completedCount: Long = 0,
    val totalLatencyMs: Long = 0,
    val promptTokens: Long = 0,
    val completionTokens: Long = 0,
    val perProvider: Map<String, ProviderInferenceMetrics> = emptyMap(),
)

@Serializable
data class ProviderInferenceMetrics(
    val completedCount: Long = 0,
    val totalLatencyMs: Long = 0,
    val promptTokens: Long = 0,
    val completionTokens: Long = 0,
)

@Serializable
data class ToolMetrics(
    val completedCount: Long = 0,
    val totalDurationMs: Long = 0,
    val perTool: Map<String, ToolUsageMetrics> = emptyMap(),
)

@Serializable
data class ToolUsageMetrics(
    val completedCount: Long = 0,
    val totalDurationMs: Long = 0,
)

@Serializable
data class ApprovalMetrics(
    val requestedCount: Long = 0,
    val resolvedCount: Long = 0,
    val totalWaitMs: Long = 0,
    val perTier: Map<String, TierApprovalMetrics> = emptyMap(),
)

@Serializable
data class TierApprovalMetrics(
    val requestedCount: Long = 0,
    val resolvedCount: Long = 0,
    val totalWaitMs: Long = 0,
)

/**
 * Signal-quality accumulators — raw counts of the quality/rate events other bounded contexts emit.
 * Ratios (extraction-quality, retrieval-precision) are derived at render time from these sums, the
 * same way inference throughput is, so this stays a stable replayable accumulator.
 */
@Serializable
data class QualityMetrics(
    val sourceFetches: Long = 0,          // total research extractions (extraction-quality denominator)
    val lowQualityExtractions: Long = 0,  // extractions flagged below the quality bar
    val retrievedHits: Long = 0,          // repo-knowledge hits surfaced to a stage
    val droppedHits: Long = 0,            // repo-knowledge hits filtered below the similarity threshold
    val briefDriftCount: Long = 0,        // brief-echo mismatches (uncovered reqs / hallucinated refs)
    val capabilityGapCount: Long = 0,     // NEEDS_TOOL capability-gap reflections (genuine gaps only)
)

@Serializable
data class FailureMetrics(
    val inferenceFailures: Long = 0,
    val inferenceTimeouts: Long = 0,
    val toolFailures: Long = 0,
    val toolRejections: Long = 0,
    val stageFailures: Long = 0,
    val workflowFailures: Long = 0,
)
