from django.db.models import Q

class Filter:
    def __init__(self, field, condition_type, **kwargs):
        self.field = field
        self.condition_type = condition_type
        self.value = kwargs.get("value", None)  # Single value for conditions like lte, gte, lt, gt, exact
        self.start = kwargs.get("start", None)  # Start and end for range conditions
        self.end = kwargs.get("end", None)
        self.fields = kwargs.get("fields", None)
        self.comparison = kwargs.get("comparison", None)
        self.ignore_nan = kwargs.get("ignore_nan", False)
        self.count_nan_as = kwargs.get("count_nan_as", None)
        self.label = self.generate_label()

    def generate_label(self):
        field_label = self.field.replace("_", " ").upper()
        condition_map = {
            "exact": "==",
            "gte": ">=",
            "lte": "<=",
            "gt": ">",
            "lt": "<",
            "range": "IN RANGE",
            "combined": "COMBINED",
            "sum": "SUM",
            "not_range": "NOT IN RANGE",
        }
        condition_label = condition_map.get(self.condition_type, self.condition_type.upper())
        if self.condition_type in ["gte", "lte", "gt", "lt", "exact"]:
            value = self.value if self.value is not None else "UNKNOWN"
            return f"{field_label} {condition_label} {value}"
        elif self.condition_type == "range":
            return f"{field_label} {condition_label} [{self.start}, {self.end})"
        elif self.condition_type == "combined":
            return f"{field_label} {condition_label}"
        elif self.condition_type == "sum":
            return f"{field_label} {condition_label} {self.value}"
        elif self.condition_type == "not_range":
            return f"{field_label} {condition_label} NOT [{self.start}, {self.end})"
        else:
            return f"{field_label} {condition_label}"

    def get_filter_condition(self):
        if self.condition_type == "exact":
            return Q(**{self.field: self.value})
        elif self.condition_type == "gte":
            return Q(**{f"{self.field}__gte": self.value})
        elif self.condition_type == "lte":
            return Q(**{f"{self.field}__lte": self.value})
        elif self.condition_type == "gt":
            return Q(**{f"{self.field}__gt": self.value})
        elif self.condition_type == "lt":
            return Q(**{f"{self.field}__lt": self.value})
        elif self.condition_type == "range":
            return Q(**{f"{self.field}__gte": self.start, f"{self.field}__lt": self.end})
        elif self.condition_type == "combined":
            if self.value == "Yes":
                return Q(**{f"{field}": self.value for field in self.fields})
            elif self.value == "No":
                return ~Q(**{f"{field}": "Yes" for field in self.fields})
        elif self.condition_type == "sum":
            return Q(**{f"{field}__{self.comparison}": self.value for field in self.fields})
        elif self.condition_type == "not_range":
            return ~Q(**{f"{self.field}__gte": self.start, f"{self.field}__lt": self.end})
        else:
            raise ValueError(f"Unknown filter type: {self.condition_type}")

    def __repr__(self):
        return f"Filter(field={self.field}, label={self.label}, type={self.condition_type})"

class Group:
    def __init__(self, name, filters=None, logic="AND"):
        self.name = name  # Unique name for the group
        self.filters = filters or []  # List of Filter or Group objects
        self.logic = logic.upper()  # Logic operator: "AND" or "OR"

    def add_filter(self, filter_obj):
        if not isinstance(filter_obj, (Filter, Group)):
            raise TypeError("Only Filter or Group objects can be added.")
        self.filters.append(filter_obj)

    def get_combined_condition(self):
        if not self.filters:
            raise ValueError("No filters in the group to combine.")

        combined_condition = Q()

        if self.logic == "AND":
            for filter_obj in self.filters:
                combined_condition &= (
                    filter_obj.get_combined_condition()
                    if isinstance(filter_obj, Group)
                    else filter_obj.get_filter_condition()
                )
        elif self.logic == "OR":
            for filter_obj in self.filters:
                combined_condition |= (
                    filter_obj.get_combined_condition()
                    if isinstance(filter_obj, Group)
                    else filter_obj.get_filter_condition()
                )
        else:
            raise ValueError(f"Invalid logic operator: {self.logic}")

        return combined_condition

    def __repr__(self):
        return f"Group(name={self.name}, logic={self.logic}, filters={self.filters})"
