import json

import pyarrow as pa
import pyarrow.compute as pc


def apply_columns(table: pa.Table, columns_param: str | None) -> pa.Table:
    if not columns_param:
        return table
    names = [c.strip() for c in columns_param.split(",") if c.strip()]
    valid = [n for n in names if n in table.schema.names]
    if not valid:
        return table
    return table.select(valid)


_OPS = {
    "=": pc.equal,
    "!=": pc.not_equal,
    ">": pc.greater,
    ">=": pc.greater_equal,
    "<": pc.less,
    "<=": pc.less_equal,
}


def apply_filters(table: pa.Table, filters_param: str | None) -> pa.Table:
    if not filters_param:
        return table
    try:
        predicates = json.loads(filters_param)
    except (json.JSONDecodeError, TypeError):
        return table
    if not isinstance(predicates, list):
        return table

    mask: pa.ChunkedArray | None = None

    for pred in predicates:
        if not isinstance(pred, dict):
            continue
        col = pred.get("col")
        op = pred.get("op")
        if col not in table.schema.names or op is None:
            continue

        column = table.column(col)

        if op == "is_null":
            m = pc.is_null(column)
        elif op == "is_not_null":
            m = pc.is_valid(column)
        elif op == "in":
            value = pred.get("value")
            if not isinstance(value, list):
                continue
            m = pc.is_in(column, pa.array(value))
        elif op in _OPS:
            value = pred.get("value")
            if value is None:
                continue
            m = _OPS[op](column, pa.scalar(value))
        else:
            continue

        mask = m if mask is None else pc.and_(mask, m)

    if mask is None:
        return table
    return table.filter(mask)
