"""
Parquet schema introspection without reading column data (footer / metadata only).
"""

from __future__ import annotations

from typing import FrozenSet, List, Optional, Sequence, Union

try:
    import pyarrow as pa
    import pyarrow.parquet as pq
except ImportError:  # pragma: no cover
    pa = None  # type: ignore
    pq = None  # type: ignore


def norm_col(name: Union[str, bytes]) -> str:
    if isinstance(name, bytes):
        return name.decode("utf-8", errors="replace").strip()
    return str(name).strip()


def schema_column_names(path: str) -> Optional[FrozenSet[str]]:
    """
    Return column names from the Parquet footer (no data pages read).

    Returns None if the path is unreadable or not valid Parquet.
    """
    if pq is None:
        return None
    try:
        sch = pq.read_schema(path)
        return frozenset(norm_col(n) for n in sch.names)
    except Exception:
        return None


def column_is_integer_type(path: str, column: str) -> bool:
    """True if *column* exists and has an integer Arrow type (safe for numeric range pushdown)."""
    if pa is None or pq is None:
        return False
    col = norm_col(column)
    try:
        sch = pq.read_schema(path)
        try:
            field = sch.field(col)
        except KeyError:
            return False
        return pa.types.is_integer(field.type)
    except Exception:
        return False


def intersect_columns(path: str, wanted: Sequence[str]) -> Optional[List[str]]:
    """
    Ordered intersection of *wanted* with columns present in the file.

    * None — schema could not be read (caller may fall back to a full read).
    * [] — schema read OK but no overlap (caller may fall back to a full read).
    * non-empty — use as ``columns=`` for a pruned read.
    """
    names = schema_column_names(path)
    if names is None:
        return None
    w = [norm_col(c) for c in wanted]
    return [c for c in w if c in names]


def schema_has_all(names: FrozenSet[str], required: Sequence[str]) -> bool:
    """True if every *required* column exists in *names* (normalized)."""
    n = {norm_col(x) for x in names}
    return all(norm_col(r) in n for r in required)


def schema_matches_any_group(
    names: FrozenSet[str], groups: Sequence[Sequence[str]]
) -> bool:
    """True if at least one *group* is fully contained in *names*."""
    n = {norm_col(x) for x in names}
    for g in groups:
        if all(norm_col(c) in n for c in g):
            return True
    return False
