//! Adaptive FEC configuration — maps `QualityProfile` to FEC encoder parameters.

use wzp_proto::QualityProfile;

use crate::encoder::RaptorQFecEncoder;

/// Adaptive FEC configuration derived from a `QualityProfile`.
#[derive(Clone, Debug)]
pub struct AdaptiveFec {
    /// Frames per FEC block.
    pub frames_per_block: usize,
    /// Repair ratio (0.0 = none, 1.0 = 100% overhead).
    pub repair_ratio: f32,
    /// Symbol size in bytes.
    pub symbol_size: u16,
    /// Repair ratio to use when the block contains a keyframe.
    /// Default 0.5 (50% overhead) — keyframes are critical and worth
    /// the extra bandwidth.
    pub keyframe_repair_ratio: f32,
}

impl AdaptiveFec {
    /// Default symbol size for adaptive configuration.
    const DEFAULT_SYMBOL_SIZE: u16 = 256;
    /// Default keyframe repair ratio (PRD-video-v1 T4.5).
    const DEFAULT_KEYFRAME_REPAIR_RATIO: f32 = 0.5;

    /// Create an adaptive FEC configuration from a quality profile.
    ///
    /// Maps quality tiers:
    /// - GOOD: 5 frames/block, 20% repair
    /// - DEGRADED: 10 frames/block, 50% repair
    /// - CATASTROPHIC: 8 frames/block, 100% repair
    pub fn from_profile(profile: &QualityProfile) -> Self {
        Self {
            frames_per_block: profile.frames_per_block as usize,
            repair_ratio: profile.fec_ratio,
            symbol_size: Self::DEFAULT_SYMBOL_SIZE,
            keyframe_repair_ratio: Self::DEFAULT_KEYFRAME_REPAIR_RATIO,
        }
    }

    /// Build a configured FEC encoder from this adaptive configuration.
    pub fn build_encoder(&self) -> RaptorQFecEncoder {
        let mut enc = RaptorQFecEncoder::new(self.frames_per_block, self.symbol_size);
        enc.set_keyframe_ratio(self.keyframe_repair_ratio);
        enc
    }

    /// Get the repair ratio for use with `FecEncoder::generate_repair()`.
    pub fn ratio(&self) -> f32 {
        self.repair_ratio
    }

    /// Estimated overhead factor (1.0 + repair_ratio).
    pub fn overhead_factor(&self) -> f32 {
        1.0 + self.repair_ratio
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use wzp_proto::FecEncoder;

    #[test]
    fn good_profile() {
        let cfg = AdaptiveFec::from_profile(&QualityProfile::GOOD);
        assert_eq!(cfg.frames_per_block, 5);
        assert!((cfg.repair_ratio - 0.2).abs() < f32::EPSILON);
        assert!((cfg.keyframe_repair_ratio - 0.5).abs() < f32::EPSILON);
    }

    #[test]
    fn degraded_profile() {
        let cfg = AdaptiveFec::from_profile(&QualityProfile::DEGRADED);
        assert_eq!(cfg.frames_per_block, 10);
        assert!((cfg.repair_ratio - 0.5).abs() < f32::EPSILON);
    }

    #[test]
    fn catastrophic_profile() {
        let cfg = AdaptiveFec::from_profile(&QualityProfile::CATASTROPHIC);
        assert_eq!(cfg.frames_per_block, 8);
        assert!((cfg.repair_ratio - 1.0).abs() < f32::EPSILON);
    }

    #[test]
    fn build_encoder_from_profile() {
        let cfg = AdaptiveFec::from_profile(&QualityProfile::DEGRADED);
        let encoder = cfg.build_encoder();
        assert_eq!(encoder.current_block_size(), 0);
        assert_eq!(wzp_proto::FecEncoder::current_block_id(&encoder), 0);
    }

    #[test]
    fn overhead_factor() {
        let cfg = AdaptiveFec::from_profile(&QualityProfile::CATASTROPHIC);
        assert!((cfg.overhead_factor() - 2.0).abs() < f32::EPSILON);
    }
}
