from collections.abc import Iterator

import fiona

from geo.errors import FilterError


def parse_bbox(bbox_str: str) -> tuple[float, float, float, float]:
    """Parse a 'west,south,east,north' string into a bbox tuple."""
    parts = bbox_str.split(",")
    if len(parts) != 4:
        raise FilterError(f"Bounding box must have 4 values (w,s,e,n), got {len(parts)}")
    try:
        return tuple(float(p.strip()) for p in parts)
    except ValueError as e:
        raise FilterError(f"Invalid bbox value: {e}")


def filter_features(path: str, bbox: tuple[float, float, float, float] | None = None,
                    where: str | None = None,
                    layer: str | None = None) -> Iterator[dict]:
    """Yield features matching spatial and/or attribute criteria.

    Filtering is pushed down to OGR for performance (spatial index usage).
    """
    kwargs = {}
    if layer is not None:
        kwargs["layer"] = layer

    with fiona.open(path, "r", **kwargs) as src:
        if bbox and where:
            for feature in src.filter(bbox=bbox, where=where):
                yield feature
        elif bbox:
            for feature in src.filter(bbox=bbox):
                yield feature
        elif where:
            for feature in src.filter(where=where):
                yield feature
        else:
            for feature in src:
                yield feature
