from abc import ABC
from typing import Any, Callable, Dict, List

from starlette.requests import Request


class RequestParamsMixin:
    """
    Mixin that provides helpers around `self.request`,
    such as fetching typed query parameters.
    """
    request: Request | None = None

    def get_query_param(self, param: str, default: Any = None, param_type: type = str):
        """
        Helper method to get query parameters from request.
        """
        if not self.request or not self.request.query_params:
            return default

        value = self.request.query_params.get(param, default)
        if value != default and param_type != str:
            try:
                return param_type(value)
            except (ValueError, TypeError):
                return default
        return value


class FieldHandlingMixin(ABC):
    """
    Mixin that provides helpers for processing field names and callbacks.
    Used by TableWidget, FormWidget, ModelWidget and related classes.
    """

    @staticmethod
    def get_field_name(field: Any) -> str:
        """Extract the field name from various field representations."""
        if isinstance(field, str):
            return field
        elif hasattr(field, 'key'):
            return field.key  # SQLAlchemy Column object
        elif hasattr(field, 'fget'):
            return field.fget.__name__  # SQLAlchemy hybrid property
        elif hasattr(field, '__name__'):
            return field.__name__  # Regular property or method
        else:
            raise ValueError(f"Unsupported field type: {field}")

    @staticmethod
    def process_field_callbacks(
        field_callbacks: Dict[Any, Callable[[Any], Any]]
    ) -> Dict[Any, Callable[[Any], Any]]:
        """Process field_callbacks to ensure keys are field names."""
        processed: Dict[Any, Callable[[Any], Any]] = {}
        for key, callback in field_callbacks.items():
            processed[FieldHandlingMixin.get_field_name(key)] = callback
        return processed

    @staticmethod
    def process_fields_names(list_fields: List[Any]) -> List[str]:
        """Process a list of field references into plain string names."""
        return [FieldHandlingMixin.get_field_name(f) for f in list_fields]
