use crate::models::tracker::{Filter, FilterGroup};
use crate::services::tuleap_client::extract_artifact_field_values;

pub fn apply_filters(
    artifacts: &[serde_json::Value],
    filter_groups: &[FilterGroup],
) -> Vec<serde_json::Value> {
    if filter_groups.is_empty() {
        return artifacts.to_vec();
    }
    artifacts
        .iter()
        .filter(|a| matches_all_groups(a, filter_groups))
        .cloned()
        .collect()
}

fn matches_all_groups(artifact: &serde_json::Value, groups: &[FilterGroup]) -> bool {
    groups.iter().all(|g| matches_any_condition(artifact, &g.conditions))
}

fn matches_any_condition(artifact: &serde_json::Value, conditions: &[Filter]) -> bool {
    if conditions.is_empty() {
        return true;
    }
    conditions.iter().any(|c| matches_condition(artifact, c))
}

fn matches_condition(artifact: &serde_json::Value, condition: &Filter) -> bool {
    let field_values = extract_artifact_field_values(artifact, &condition.field);
    match condition.operator.as_str() {
        "Equals" => {
            condition.value.len() == 1
                && field_values.iter().any(|v| v == &condition.value[0])
        }
        "NotEquals" => {
            condition.value.len() == 1
                && !field_values.iter().any(|v| v == &condition.value[0])
        }
        "In" => field_values.iter().any(|v| condition.value.contains(v)),
        "NotIn" => !field_values.iter().any(|v| condition.value.contains(v)),
        _ => false,
    }
}

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

    fn make_artifact(status: &str, assigned: &str, priority: &str) -> serde_json::Value {
        json!({
            "id": 123,
            "title": "Test ticket",
            "values": [
                {
                    "field_id": 1,
                    "label": "Status",
                    "type": "sb",
                    "values": [{ "id": 1, "label": status }]
                },
                {
                    "field_id": 2,
                    "label": "Assigned to",
                    "type": "msb",
                    "bind_value_objects": [{ "id": 2, "display_name": assigned }]
                },
                {
                    "field_id": 3,
                    "label": "Priority",
                    "type": "sb",
                    "values": [{ "id": 3, "label": priority }]
                }
            ]
        })
    }

    #[test]
    fn test_empty_filters_returns_all() {
        let artifacts = vec![
            make_artifact("Nouveau", "Alice", "Haute"),
            make_artifact("Ferme", "Bob", "Basse"),
        ];
        let result = apply_filters(&artifacts, &[]);
        assert_eq!(result.len(), 2);
    }

    #[test]
    fn test_single_in_filter() {
        let artifacts = vec![
            make_artifact("Nouveau", "Alice", "Haute"),
            make_artifact("Ferme", "Bob", "Basse"),
        ];
        let groups = vec![FilterGroup {
            conditions: vec![Filter {
                field: "Status".to_string(),
                operator: "In".to_string(),
                value: vec!["Nouveau".to_string()],
            }],
        }];
        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 1);
        assert_eq!(result[0]["values"][0]["values"][0]["label"], "Nouveau");
    }

    #[test]
    fn test_or_within_group() {
        let artifacts = vec![
            make_artifact("Nouveau", "Alice", "Haute"),
            make_artifact("A traiter", "Bob", "Moyenne"),
            make_artifact("Ferme", "Carol", "Basse"),
        ];
        let groups = vec![FilterGroup {
            conditions: vec![
                Filter {
                    field: "Status".to_string(),
                    operator: "Equals".to_string(),
                    value: vec!["Nouveau".to_string()],
                },
                Filter {
                    field: "Status".to_string(),
                    operator: "Equals".to_string(),
                    value: vec!["A traiter".to_string()],
                },
            ],
        }];
        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 2);
    }

    #[test]
    fn test_and_across_groups() {
        let artifacts = vec![
            make_artifact("Nouveau", "Team Maintenance", "Haute"),
            make_artifact("A traiter", "Other Team", "Moyenne"),
            make_artifact("Ferme", "Team Maintenance", "Basse"),
        ];
        let groups = vec![
            FilterGroup {
                conditions: vec![Filter {
                    field: "Status".to_string(),
                    operator: "In".to_string(),
                    value: vec!["Nouveau".to_string(), "A traiter".to_string()],
                }],
            },
            FilterGroup {
                conditions: vec![Filter {
                    field: "Assigned to".to_string(),
                    operator: "In".to_string(),
                    value: vec!["Team Maintenance".to_string()],
                }],
            },
        ];
        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 1);
        assert_eq!(result[0]["values"][0]["values"][0]["label"], "Nouveau");
    }

    #[test]
    fn test_not_in_filter() {
        let artifacts = vec![
            make_artifact("Nouveau", "Alice", "Haute"),
            make_artifact("Ferme", "Bob", "Basse"),
            make_artifact("Ferme", "Carol", "Moyenne"),
        ];
        let groups = vec![FilterGroup {
            conditions: vec![Filter {
                field: "Status".to_string(),
                operator: "NotIn".to_string(),
                value: vec!["Ferme".to_string()],
            }],
        }];
        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 1);
        assert_eq!(result[0]["values"][0]["values"][0]["label"], "Nouveau");
    }

    #[test]
    fn test_equals_filter() {
        let artifacts = vec![
            make_artifact("Nouveau", "Alice", "Haute"),
            make_artifact("Nouveau", "Bob", "Basse"),
            make_artifact("Ferme", "Carol", "Haute"),
        ];
        let groups = vec![FilterGroup {
            conditions: vec![Filter {
                field: "Priority".to_string(),
                operator: "Equals".to_string(),
                value: vec!["Haute".to_string()],
            }],
        }];
        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 2);
    }

    #[test]
    fn test_no_match_returns_empty() {
        let artifacts = vec![
            make_artifact("Nouveau", "Alice", "Haute"),
            make_artifact("A traiter", "Bob", "Moyenne"),
        ];
        let groups = vec![FilterGroup {
            conditions: vec![Filter {
                field: "Status".to_string(),
                operator: "Equals".to_string(),
                value: vec!["Ferme".to_string()],
            }],
        }];
        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn test_in_filter_matches_on_value_id() {
        let artifacts = vec![
            json!({
                "id": 123,
                "title": "A",
                "values": [
                    {
                        "field_id": 1,
                        "label": "Status",
                        "type": "sb",
                        "values": [{ "id": 1, "label": "Nouveau" }]
                    }
                ]
            }),
            json!({
                "id": 124,
                "title": "B",
                "values": [
                    {
                        "field_id": 1,
                        "label": "Status",
                        "type": "sb",
                        "values": [{ "id": 9, "label": "Ferme" }]
                    }
                ]
            }),
        ];
        let groups = vec![FilterGroup {
            conditions: vec![Filter {
                field: "Status".to_string(),
                operator: "In".to_string(),
                value: vec!["1".to_string()],
            }],
        }];

        let result = apply_filters(&artifacts, &groups);
        assert_eq!(result.len(), 1);
        assert_eq!(result[0]["values"][0]["values"][0]["label"], "Nouveau");
    }
}
