import json
import re

_SAFE_COL = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")

_SQL_OPS = {
    "=": "=",
    "!=": "!=",
    ">": ">",
    ">=": ">=",
    "<": "<",
    "<=": "<=",
}


def build_query(table_name: str, columns: str | None, filters: str | None) -> tuple[str, list]:
    """Build a parameterized SELECT query. Returns (sql, params)."""
    cols = _build_columns(columns)
    where, params = _build_where(filters)
    sql = f'SELECT {cols} FROM db."{table_name}"{where}'
    return sql, params


def _build_columns(columns_param: str | None) -> str:
    if not columns_param:
        return "*"
    names = [c.strip() for c in columns_param.split(",") if c.strip()]
    valid = [f'"{n}"' for n in names if _SAFE_COL.match(n)]
    if not valid:
        return "*"
    return ", ".join(valid)


def _build_where(filters_param: str | None) -> tuple[str, list]:
    if not filters_param:
        return "", []

    try:
        predicates = json.loads(filters_param)
    except (json.JSONDecodeError, TypeError):
        return "", []

    if not isinstance(predicates, list):
        return "", []

    clauses = []
    params = []

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

        quoted_col = f'"{col}"'

        if op == "is_null":
            clauses.append(f"{quoted_col} IS NULL")
        elif op == "is_not_null":
            clauses.append(f"{quoted_col} IS NOT NULL")
        elif op == "in":
            value = pred.get("value")
            if not isinstance(value, list) or not value:
                continue
            placeholders = ", ".join(["?"] * len(value))
            clauses.append(f"{quoted_col} IN ({placeholders})")
            params.extend(value)
        elif op in _SQL_OPS:
            value = pred.get("value")
            if value is None:
                continue
            clauses.append(f"{quoted_col} {_SQL_OPS[op]} ?")
            params.append(value)

    if not clauses:
        return "", []

    return " WHERE " + " AND ".join(clauses), params
