use crate::consts::ENUM_NULL;
use crate::data::EnumFeatureData;

pub struct ParsedFilter {
    pub feat_idx: usize,
    pub min: f32,
    pub max: f32,
}

pub struct ParsedEnumFilter {
    pub enum_idx: usize,
    pub allowed: Vec<u8>,
}

/// Parse comma-separated filter string into numeric and enum filters.
/// Numeric format: `name:min:max`
/// Enum format: `name:val1|val2|val3` (pipe-separated values)
pub fn parse_filters(
    filter_str: Option<&str>,
    feature_names: &[String],
    enum_features: &[EnumFeatureData],
) -> (Vec<ParsedFilter>, Vec<ParsedEnumFilter>) {
    let mut numeric = Vec::new();
    let mut enums = Vec::new();

    let input = match filter_str.filter(|text| !text.is_empty()) {
        Some(text) => text,
        None => return (numeric, enums),
    };

    for entry in input.split(',') {
        let parts: Vec<&str> = entry.splitn(2, ':').collect();
        if parts.len() != 2 {
            continue;
        }
        let name = parts[0].trim();
        let rest = parts[1].trim();

        if let Some(enum_idx) = enum_features
            .iter()
            .position(|enum_feat| enum_feat.name == name)
        {
            let enum_feat = &enum_features[enum_idx];
            let allowed: Vec<u8> = rest
                .split('|')
                .filter_map(|value| {
                    let value = value.trim();
                    enum_feat
                        .values
                        .iter()
                        .position(|existing| existing == value)
                        .map(|position| position as u8)
                })
                .collect();
            enums.push(ParsedEnumFilter { enum_idx, allowed });
        } else {
            let num_parts: Vec<&str> = rest.splitn(2, ':').collect();
            if num_parts.len() != 2 {
                continue;
            }
            let min = match num_parts[0].trim().parse::<f32>() {
                Ok(value) => value,
                Err(_) => continue,
            };
            let max = match num_parts[1].trim().parse::<f32>() {
                Ok(value) => value,
                Err(_) => continue,
            };
            if let Some(feat_idx) = feature_names.iter().position(|feat_name| feat_name == name) {
                numeric.push(ParsedFilter { feat_idx, min, max });
            }
        }
    }

    (numeric, enums)
}

pub fn row_passes_filters(
    row: usize,
    filters: &[ParsedFilter],
    enum_filters: &[ParsedEnumFilter],
    feature_data: &[f32],
    num_features: usize,
    enum_data: &[u8],
    num_enums: usize,
) -> bool {
    filters.iter().all(|filter| {
        let value = feature_data[row * num_features + filter.feat_idx];
        value.is_finite() && value >= filter.min && value <= filter.max
    }) && enum_filters.iter().all(|enum_filter| {
        let value = enum_data[row * num_enums + enum_filter.enum_idx];
        value != ENUM_NULL && enum_filter.allowed.contains(&value)
    })
}
