//! The determinism tier of a stage (`extensions/staged-delivery.md` §5).
//!
//! A unit's tier is the weakest step of its chain: the base's decode, then each layer
//! applied to it. A layer acts on reconstructed output, so `entropy-consistency`, which
//! fixes only the entropy-decoded symbols, does not pass through it
//! (`extensions/derived-tracks.md` §3 rules 1 and 2). [`compose`] is a pure function,
//! so the rule is tested over every combination, whether or not any admitted base
//! holds a given tier.

use crate::codecs::{capability_replicate_class, DeterminismTier, ReplicateClass};
use crate::container::Prior;

/// The tier of a chain: `base`, then each of `steps` in order, where
/// `on_reconstruction[i]` says whether step `i` acts on reconstructed output. The
/// weakest tier wins, and a step on reconstructed output over `entropy-consistency`
/// gives no tier. `None` is no tier. A step whose `on_reconstruction` flag is missing
/// counts as acting on reconstructed output.
pub fn compose(base: Option<DeterminismTier>, steps: &[Option<DeterminismTier>], on_reconstruction: &[bool]) -> Option<DeterminismTier> {
    let mut t = base;
    for (i, step) in steps.iter().enumerate() {
        let recon = on_reconstruction.get(i).copied().unwrap_or(true);
        t = match (t, *step) {
            (None, _) | (_, None) => None,
            (Some(DeterminismTier::EntropyConsistency), Some(_)) if recon => None,
            (Some(a), Some(b)) => Some(a.min(b)),
        };
    }
    t
}

/// The tier of a base's step (§5): `decode-equivalence` when `cap`'s registry class is
/// `StandardDefined`, or `NeuralIntegerExact` with a pin that resolved (`pin` is the
/// pin a sink verified, `None` when the decode was unpinned) and declares
/// `decode-equivalence`; `entropy-consistency` when the class is `NeuralEntropyExact`;
/// otherwise none.
pub fn base_tier(cap: &str, pin: Option<&Prior>) -> Option<DeterminismTier> {
    match capability_replicate_class(cap)? {
        ReplicateClass::StandardDefined => Some(DeterminismTier::DecodeEquivalence),
        ReplicateClass::NeuralIntegerExact => {
            pin.and_then(Prior::declared_tier).filter(|t| *t == DeterminismTier::DecodeEquivalence)
        }
        ReplicateClass::NeuralEntropyExact => Some(DeterminismTier::EntropyConsistency),
        _ => None,
    }
}

/// The token a tier is written as: `decode-equivalence`, `entropy-consistency`, or
/// `none` for no tier.
pub fn token(t: Option<DeterminismTier>) -> &'static str {
    match t {
        Some(DeterminismTier::DecodeEquivalence) => "decode-equivalence",
        Some(DeterminismTier::EntropyConsistency) => "entropy-consistency",
        None => "none",
    }
}

/// The tier a token names; `none` and anything else is no tier.
pub fn from_token(s: &str) -> Option<DeterminismTier> {
    match s {
        "decode-equivalence" => Some(DeterminismTier::DecodeEquivalence),
        "entropy-consistency" => Some(DeterminismTier::EntropyConsistency),
        _ => None,
    }
}
