//! Domain representations for reverse evaluation
//!
//! This module defines domain constraints that represent the possible input values
//! for variables based on desired output values during reverse evaluation.

use crate::value::Value;

#[derive(Debug, Clone, PartialEq)]
pub enum Constraint {
    Empty,
    Universal,
    Equals(Value),

    // TODO: Refactor this to be a Rust RangeInclusive
    Range {
        min: Option<Value>,
        max: Option<Value>,
    },
}

impl Constraint {
    /// Check if this constraint is satisfied by a given value
    pub fn is_satisfied_by(&self, value: &Value) -> bool {
        match self {
            Constraint::Equals(expected) => value == expected,
            Constraint::Range { min, max } => {
                if let Some(min_val) = min {
                    if !value.gte(min_val) {
                        return false;
                    }
                }
                if let Some(max_val) = max {
                    if !value.lte(max_val) {
                        return false;
                    }
                }
                true
            }
            Constraint::Empty => false,
            Constraint::Universal => true,
        }
    }

    /// Map a function over all values in the constraint
    ///
    /// This applies the given function to each value in the constraint:
    /// - Empty and Universal are returned unchanged
    /// - Equals applies f to the single value
    /// - Range applies f to both min and max (if present)
    pub fn map<F, E>(&self, f: F) -> Result<Constraint, E>
    where
        F: Fn(&Value) -> Result<Value, E>,
    {
        match self {
            Constraint::Empty => Ok(Constraint::Empty),
            Constraint::Universal => Ok(Constraint::Universal),
            Constraint::Equals(v) => Ok(Constraint::Equals(f(v)?)),
            Constraint::Range { min, max } => {
                let new_min = min.as_ref().map(&f).transpose()?;
                let new_max = max.as_ref().map(&f).transpose()?;
                Ok(Constraint::Range {
                    min: new_min,
                    max: new_max,
                })
            }
        }
    }
}
