use super::super::*;

#[cfg(test)]
pub(in super::super) fn eval_between_function(
    function_name: &str,
    args: Vec<StoredValue>,
    negated: bool,
) -> Result<StoredValue> {
    if args.len() != 3 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 3 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let value = iter.next().unwrap();
    let low = iter.next().unwrap();
    let high = iter.next().unwrap();

    let ge_low = match compare_values(&value, &low)? {
        Some(Ordering::Greater | Ordering::Equal) => Some(true),
        Some(Ordering::Less) => Some(false),
        None => None,
    };
    let le_high = match compare_values(&value, &high)? {
        Some(Ordering::Less | Ordering::Equal) => Some(true),
        Some(Ordering::Greater) => Some(false),
        None => None,
    };
    let result = match (ge_low, le_high) {
        (Some(false), _) | (_, Some(false)) => Some(false),
        (Some(true), Some(true)) => Some(true),
        _ => None,
    };

    match result {
        Some(result) => Ok(StoredValue::Boolean(if negated { !result } else { result })),
        None => Ok(StoredValue::Null),
    }
}

pub(in super::super) fn eval_in_list_function(
    function_name: &str,
    args: Vec<StoredValue>,
    negated: bool,
) -> Result<StoredValue> {
    if args.is_empty() {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects at least 1 arg, got 0",
            function_name
        )));
    }

    let mut iter = args.into_iter();
    let value = iter.next().unwrap();
    let list = iter.collect::<Vec<_>>();

    if list.is_empty() {
        return Ok(StoredValue::Boolean(negated));
    }
    if value.is_null() {
        return Ok(StoredValue::Null);
    }

    let mut has_null = false;
    for item in list {
        match values_equal(&value, &item)? {
            Some(true) => {
                return Ok(StoredValue::Boolean(!negated));
            }
            Some(false) => {}
            None => has_null = true,
        }
    }

    if has_null {
        Ok(StoredValue::Null)
    } else {
        Ok(StoredValue::Boolean(negated))
    }
}

pub(in super::super) fn eval_is_test_function(
    function_name: &str,
    args: Vec<StoredValue>,
    test: InternalIsTestKind,
    negated: bool,
) -> Result<StoredValue> {
    if args.len() != 1 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 1 arg, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let value = iter.next().unwrap();
    let result = match test {
        InternalIsTestKind::True => matches!(value, StoredValue::Boolean(true)),
        InternalIsTestKind::False => matches!(value, StoredValue::Boolean(false)),
        InternalIsTestKind::Unknown => value.is_null(),
    };

    Ok(StoredValue::Boolean(if negated { !result } else { result }))
}

pub(in super::super) fn eval_is_distinct_from_function(
    function_name: &str,
    args: Vec<StoredValue>,
    negated: bool,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let left = iter.next().unwrap();
    let right = iter.next().unwrap();

    let not_distinct = match (&left, &right) {
        (StoredValue::Null, StoredValue::Null) => true,
        (StoredValue::Null, _) | (_, StoredValue::Null) => false,
        _ => matches!(values_equal(&left, &right)?, Some(true)),
    };

    Ok(StoredValue::Boolean(if negated {
        not_distinct
    } else {
        !not_distinct
    }))
}
