from __future__ import annotations

from collections.abc import Mapping
from typing import Any


def row_to_mapping(row: Any) -> dict[str, Any]:
    """Best-effort conversion of a Snowpark row-like object to a plain dict."""

    if isinstance(row, Mapping):
        return dict(row)

    for meth in ("asDict", "as_dict", "asdict"):
        fn = getattr(row, meth, None)
        if callable(fn):
            try:
                out = fn()
            except Exception:
                out = None
            if isinstance(out, Mapping):
                return dict(out)

    try:
        out = dict(row)
    except Exception:
        return {}
    return out if isinstance(out, dict) else {}


def row_get(row: Any, *keys: str, default: Any = None, case_insensitive: bool = True) -> Any:
    """Best-effort lookup of one logical field across row-like SQL result shapes.

    The ``keys`` arguments are tried in order so callers can provide fallback
    column names when SQL engines, wrappers, or mocks expose the same value
    under slightly different field names.
    """

    mapping = row_to_mapping(row)
    for key in keys:
        if key in mapping:
            return mapping[key]

    if case_insensitive and mapping:
        lowered = {str(k).strip().lower(): v for k, v in mapping.items()}
        for key in keys:
            lookup = str(key).strip().lower()
            if lookup in lowered:
                return lowered[lookup]

    for key in keys:
        value = getattr(row, key, None)
        if value is not None:
            return value

    if case_insensitive:
        attrs = {str(name).strip().lower(): name for name in dir(row)}
        for key in keys:
            lookup = str(key).strip().lower()
            name = attrs.get(lookup)
            if name:
                value = getattr(row, name, None)
                if value is not None:
                    return value

    return default


def row_first_value(row: Any, default: Any = None) -> Any:
    """Best-effort access to the first column of a SQL result row."""

    try:
        return row[0]  # type: ignore[index]
    except Exception:
        pass

    mapping = row_to_mapping(row)
    if mapping:
        return next(iter(mapping.values()), default)

    return default


__all__ = ["row_first_value", "row_get", "row_to_mapping"]
