from typing import List

import numpy as np
import pandas as pd
from typing_extensions import Literal

from solutions.graph.models import FilterMetadata


def apply_filter_rules(dataframe: pd.DataFrame, rules: List[FilterMetadata], filter_association: Literal["and", "or"]):
    if len(rules) > 0:
        cond = []
        for rule in rules:
            operator = rule.get("operator")
            if operator == "matches":
                cond.append(dataframe[rule["column"]].astype(str) == rule["value"])
            elif operator == "excludes":
                cond.append(dataframe[rule["column"]].astype(str) != rule["value"])
            elif operator == "above":
                cond.append(dataframe[rule["column"]] > float(rule["value"]))
            elif operator == "below":
                cond.append(dataframe[rule["column"]] < float(rule["value"]))
            elif operator == "range":
                cond.append(
                    dataframe[rule["column"]].between(
                        float(rule["value"]), float(rule["max_value"]), inclusive="neither"
                    )
                )
        if filter_association == "and":
            return dataframe.loc[np.logical_and.reduce(cond)]
        else:
            return dataframe.loc[np.logical_or.reduce(cond)]
    else:
        return dataframe
