from typing import Callable, Any
from ..models import Users
from sqlalchemy.orm import joinedload
from abs_exception_core.exceptions import NotFoundError, ValidationError
from abs_utils.logger import setup_logger
from ..util.db_error_handler import db_guard

logger = setup_logger(__name__)

# What to tell the caller when the query ran fine but matched no row. Keyed by
# the attribute searched on, so the message names the input that was wrong
# without ever naming a column of the users table.
NOT_FOUND_MESSAGES = {
    "email": "No account is registered with this email address.",
    "uuid": "The requested user does not exist.",
    "id": "The requested user does not exist.",
}
DEFAULT_NOT_FOUND_MESSAGE = "The requested user does not exist."


def get_user_by_attribute(db_session: Callable[...,Any],attribute: str, value: str):
    """
    Get a user by an attribute.

    Args:
        attribute (str): The attribute to get the user by.
        value (str): The value of the attribute.

    Returns:
        User: The user object if found.

    Raises:
        ValidationError: The attribute is not a column on the User model.
        NotFoundError: No matching user exists.
        HTTPException: A generic error if the query itself fails — the driver
            error is logged, never returned (see `util.db_error_handler`).
    """
    if not hasattr(Users, attribute):
        logger.error(f"Unsupported user lookup attribute requested: {attribute!r}")
        raise ValidationError(detail=DEFAULT_NOT_FOUND_MESSAGE)

    with db_session() as session:
        with db_guard("user lookup", attribute=attribute):
            user = session.query(Users).filter(
                getattr(Users, attribute) == value,
                Users.deleted_at.is_(None)  # Filter out soft-deleted users
            ).options(joinedload(Users.roles)).first()

            if not user:
                raise NotFoundError(
                    detail=NOT_FOUND_MESSAGES.get(attribute, DEFAULT_NOT_FOUND_MESSAGE)
                )
            return user
