use crate::base::state;

pub trait StateValidityChecker<S: state::State> {
    fn is_valid(&self, state: &S) -> bool;
}

pub struct DimensionBoundsChecker {
    pub bounds_per_dimension: Vec<(f64, f64)>,
}

impl StateValidityChecker<state::RealVectorState> for DimensionBoundsChecker {
    fn is_valid(&self, state: &state::RealVectorState) -> bool {
        if state.values.len() != self.bounds_per_dimension.len() {
            return false;
        }
        for (i, &value) in state.values.iter().enumerate() {
            let (min_b, max_b) = self.bounds_per_dimension[i];
            if value < min_b || value > max_b {
                return false;
            }
        }
        true
    }
}

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

    #[test]
    fn test_rvs_dimension_bounds_checker_is_valid_provided() {
        let rvs_db_checker = DimensionBoundsChecker {
            bounds_per_dimension: vec![(-2.0, 2.0), (-5.0, 0.0)],
        };

        let s1 = state::RealVectorState {
            values: vec![-2.0, -1.0],
        };
        let s2 = state::RealVectorState {
            values: vec![0.0, 0.0],
        };
        let s3 = state::RealVectorState {
            values: vec![1.0, -4.5],
        };
        let s4 = state::RealVectorState {
            values: vec![-2.1, -4.5],
        };
        let s5 = state::RealVectorState {
            values: vec![1.0, 1.0],
        };

        assert!(rvs_db_checker.is_valid(&s1), "s1 should be valid");
        assert!(rvs_db_checker.is_valid(&s2), "s2 should be valid");
        assert!(rvs_db_checker.is_valid(&s3), "s3 should be valid");
        assert!(
            !rvs_db_checker.is_valid(&s4),
            "s4 should be invalid (dim 0 too low)"
        );
        assert!(
            !rvs_db_checker.is_valid(&s5),
            "s5 should be invalid (dim 1 too high)"
        );
    }

    #[test]
    fn test_rvs_dimension_bounds_checker_boundary_conditions() {
        let rvs_db_checker = DimensionBoundsChecker {
            bounds_per_dimension: vec![(-2.0, 2.0), (-5.0, 0.0)],
        };

        let s_on_lower = state::RealVectorState {
            values: vec![-2.0, -5.0],
        };
        assert!(
            rvs_db_checker.is_valid(&s_on_lower),
            "State on all lower bounds should be valid"
        );
        let s_on_upper = state::RealVectorState {
            values: vec![2.0, 0.0],
        };
        assert!(
            rvs_db_checker.is_valid(&s_on_upper),
            "State on all upper bounds should be valid"
        );
        let s_below_lower_dim0 = state::RealVectorState {
            values: vec![-2.0000001, -2.5],
        };
        assert!(
            !rvs_db_checker.is_valid(&s_below_lower_dim0),
            "State just below lower bound for dim 0 should be invalid"
        );
        let s_below_lower_dim1 = state::RealVectorState {
            values: vec![0.0, -5.0000001],
        };
        assert!(
            !rvs_db_checker.is_valid(&s_below_lower_dim1),
            "State just below lower bound for dim 1 should be invalid"
        );
        let s_above_upper_dim0 = state::RealVectorState {
            values: vec![2.0000001, -2.5],
        };
        assert!(
            !rvs_db_checker.is_valid(&s_above_upper_dim0),
            "State just above upper bound for dim 0 should be invalid"
        );
        let s_above_upper_dim1 = state::RealVectorState {
            values: vec![0.0, 0.0000001],
        };
        assert!(
            !rvs_db_checker.is_valid(&s_above_upper_dim1),
            "State just above upper bound for dim 1 should be invalid"
        );
    }

    #[test]
    fn test_rvs_dimension_bounds_checker_dimension_mismatch() {
        let rvs_db_checker = DimensionBoundsChecker {
            bounds_per_dimension: vec![(-2.0, 2.0), (-5.0, 0.0)],
        };

        let s_too_few_dims = state::RealVectorState { values: vec![0.0] };
        assert!(
            !rvs_db_checker.is_valid(&s_too_few_dims),
            "State with too few dimensions should be invalid"
        );
        let s_too_many_dims = state::RealVectorState {
            values: vec![0.0, -1.0, 0.0],
        };
        assert!(
            !rvs_db_checker.is_valid(&s_too_many_dims),
            "State with too many dimensions should be invalid"
        );
    }

    #[test]
    fn test_rvs_dimension_bounds_checker_empty_bounds_and_state() {
        let checker_empty_bounds = DimensionBoundsChecker {
            bounds_per_dimension: vec![],
        };
        let s_empty = state::RealVectorState { values: vec![] };
        assert!(
            checker_empty_bounds.is_valid(&s_empty),
            "Empty state with empty bounds should be valid"
        );
        let s_non_empty = state::RealVectorState { values: vec![0.0] };
        assert!(
            !checker_empty_bounds.is_valid(&s_non_empty),
            "Non-empty state with empty bounds should be invalid (dim mismatch)"
        );
        let checker_non_empty_bounds = DimensionBoundsChecker {
            bounds_per_dimension: vec![(0.0, 1.0)],
        };
        assert!(
            !checker_non_empty_bounds.is_valid(&s_empty),
            "Empty state with non-empty bounds should be invalid (dim mismatch)"
        );
    }

    #[test]
    fn test_rvs_dimension_bounds_checker_single_dimension() {
        let rvs_db_checker = DimensionBoundsChecker {
            bounds_per_dimension: vec![(-1.0, 1.0)],
        };

        let s_valid_1d = state::RealVectorState { values: vec![0.0] };
        let s_invalid_low_1d = state::RealVectorState { values: vec![-1.1] };
        let s_invalid_high_1d = state::RealVectorState { values: vec![1.1] };
        let s_on_lower_1d = state::RealVectorState { values: vec![-1.0] };
        let s_on_upper_1d = state::RealVectorState { values: vec![1.0] };

        assert!(rvs_db_checker.is_valid(&s_valid_1d));
        assert!(!rvs_db_checker.is_valid(&s_invalid_low_1d));
        assert!(!rvs_db_checker.is_valid(&s_invalid_high_1d));
        assert!(rvs_db_checker.is_valid(&s_on_lower_1d));
        assert!(rvs_db_checker.is_valid(&s_on_upper_1d));
    }
}
