//! Type I and Type II feedback mechanisms.

use rand::Rng;

use crate::{Clause, config::prob_to_threshold};

/// # Overview
///
/// Type I feedback: reinforces patterns when y=1.
///
/// When clause fires: strengthen matching, weaken non-matching.
/// When clause doesn't fire: weaken all toward exclusion.
///
/// Uses integer threshold comparison for ~2x faster RNG checks.
pub fn type_i<R: Rng>(clause: &mut Clause, x: &[u8], fires: bool, s: f32, rng: &mut R) {
    let threshold_strengthen = prob_to_threshold((s - 1.0) / s);
    let threshold_weaken = prob_to_threshold(1.0 / s);
    let automata = clause.automata_mut();
    let n = x.len();

    if !fires {
        for a in automata.iter_mut() {
            if rng.random::<u32>() < threshold_weaken {
                a.decrement();
            }
        }
        return;
    }

    for k in 0..n {
        if x[k] == 1 {
            if rng.random::<u32>() < threshold_strengthen {
                automata[2 * k].increment();
            }
            if rng.random::<u32>() < threshold_weaken {
                automata[2 * k + 1].decrement();
            }
        } else {
            if rng.random::<u32>() < threshold_strengthen {
                automata[2 * k + 1].increment();
            }
            if rng.random::<u32>() < threshold_weaken {
                automata[2 * k].decrement();
            }
        }
    }
}

/// # Overview
///
/// Type II feedback: corrects false positives when y=0.
///
/// Activates blocking literals to prevent future misfires.
pub fn type_ii(clause: &mut Clause, x: &[u8]) {
    let automata = clause.automata_mut();

    for (k, &xk) in x.iter().enumerate() {
        if xk == 0 {
            if !automata[2 * k].action() {
                automata[2 * k].increment();
            }
        } else if !automata[2 * k + 1].action() {
            automata[2 * k + 1].increment();
        }
    }
}

/// # Overview
///
/// Boosted Type I (Type Ia): always strengthens matching when firing.
///
/// Uses integer threshold comparison for ~2x faster RNG checks.
pub fn type_ia<R: Rng>(clause: &mut Clause, x: &[u8], fires: bool, s: f32, rng: &mut R) {
    if !fires {
        type_i(clause, x, fires, s, rng);
        return;
    }

    let threshold_weaken = prob_to_threshold(1.0 / s);
    let automata = clause.automata_mut();

    for (k, &xk) in x.iter().enumerate() {
        if xk == 1 {
            automata[2 * k].increment();
            if rng.random::<u32>() < threshold_weaken {
                automata[2 * k + 1].decrement();
            }
        } else {
            automata[2 * k + 1].increment();
            if rng.random::<u32>() < threshold_weaken {
                automata[2 * k].decrement();
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::utils::rng_from_seed;

    #[test]
    fn type_i_not_firing() {
        let mut clause = Clause::new(3, 100, 1);
        let mut rng = rng_from_seed(42);

        for _ in 0..100 {
            type_i(&mut clause, &[1, 0, 1], false, 3.0, &mut rng);
        }

        assert!(clause.automata().iter().any(|a| a.state() < 100));
    }

    #[test]
    fn type_i_firing() {
        let mut clause = Clause::new(3, 50, 1);
        let mut rng = rng_from_seed(42);

        for _ in 0..200 {
            type_i(&mut clause, &[1, 0, 1], true, 3.0, &mut rng);
        }

        assert!(clause.automata()[0].action());
        assert!(clause.automata()[3].action());
    }

    #[test]
    fn type_ii_blocks() {
        let mut clause = Clause::new(3, 50, 1);

        for _ in 0..100 {
            type_ii(&mut clause, &[1, 0, 1]);
        }

        assert!(clause.automata()[1].action());
        assert!(clause.automata()[2].action());
        assert!(clause.automata()[5].action());
    }

    #[test]
    fn type_ia_not_firing() {
        // When not firing, type_ia should delegate to type_i
        let mut clause = Clause::new(3, 100, 1);
        let mut rng = rng_from_seed(42);

        for _ in 0..100 {
            type_ia(&mut clause, &[1, 0, 1], false, 3.0, &mut rng);
        }

        // Should weaken all automata (same behavior as type_i not firing)
        assert!(clause.automata().iter().any(|a| a.state() < 100));
    }

    #[test]
    fn type_ia_firing_always_strengthens() {
        // When firing, type_ia always strengthens matching literals
        let mut clause = Clause::new(3, 50, 1);
        let mut rng = rng_from_seed(42);

        for _ in 0..100 {
            type_ia(&mut clause, &[1, 0, 1], true, 3.0, &mut rng);
        }

        // For x[0]=1: always strengthen include[0] (automata[0])
        assert!(clause.automata()[0].action());
        // For x[1]=0: always strengthen negated[1] (automata[3])
        assert!(clause.automata()[3].action());
        // For x[2]=1: always strengthen include[2] (automata[4])
        assert!(clause.automata()[4].action());
    }

    #[test]
    fn type_ia_weakens_with_probability() {
        // Weakening should still happen with probability 1/s
        let mut clause = Clause::new(3, 100, 1);
        let mut rng = rng_from_seed(42);

        // First activate the literals
        for _ in 0..150 {
            clause.automata_mut()[1].increment(); // negated[0]
            clause.automata_mut()[2].increment(); // include[1]
            clause.automata_mut()[5].increment(); // negated[2]
        }

        // Now apply type_ia with x=[1,0,1]
        for _ in 0..300 {
            type_ia(&mut clause, &[1, 0, 1], true, 3.0, &mut rng);
        }

        // negated[0] (automata[1]) should have decreased (x[0]=1 -> weaken negated)
        // include[1] (automata[2]) should have decreased (x[1]=0 -> weaken include)
        // negated[2] (automata[5]) should have decreased (x[2]=1 -> weaken negated)
        // At least some should be below their initial high state
        let weakened = [1, 2, 5]
            .iter()
            .any(|&i| clause.automata()[i].state() < 200);
        assert!(weakened);
    }
}
