"""
Bulk-safe relational field classes.

These fields defer database validation to the operations layer,
enabling efficient bulk operations without N+1 queries.

Contract (DRF-aligned): queryset MUST be a real Django QuerySet or Manager.
For schema/introspection without a natural queryset at definition time, pass model=...
and we set queryset = model._default_manager.none().
"""

from django.db.models.manager import Manager
from django.db.models.query import QuerySet

from rest_framework.relations import PrimaryKeyRelatedField, SlugRelatedField


def _normalize_queryset(queryset, model=None):
    """
    Normalize to a real Django QuerySet for field.queryset.

    - QuerySet: returned as-is (if .model is set).
    - Manager: normalized to .all().
    - None: requires model=; returns model._default_manager.none().
    - Anything else: raises TypeError (no list/registry support).

    Returns:
        (QuerySet, resolved_model) for use by the field. resolved_model is the
        model class from the queryset (for consistency checks).
    """
    if queryset is None:
        if model is None:
            raise TypeError(
                "BulkPrimaryKeyRelatedField/BulkSlugRelatedField: when queryset is not provided, "
                "you must pass model=RelatedModel (we set queryset=model._default_manager.none() for introspection)."
            )
        return model._default_manager.none(), model

    if isinstance(queryset, Manager):
        queryset = queryset.all()

    if isinstance(queryset, QuerySet) and getattr(queryset, "model", None) is not None:
        resolved_model = queryset.model
        if model is not None and resolved_model is not model:
            raise TypeError(
                f"Bulk relation field: model={model.__name__} does not match queryset.model={resolved_model.__name__}. "
                "Omit model= or pass a queryset for the same model."
            )
        return queryset, resolved_model

    raise TypeError(
        "Bulk relation field queryset must be a Django QuerySet or Manager, not %s. "
        "Use model=RelatedModel for introspection-only, or pass a real QuerySet (e.g. RelatedModel.objects.all())."
        % type(queryset).__name__
    )


class BulkPrimaryKeyRelatedField(PrimaryKeyRelatedField):
    """
    A PrimaryKeyRelatedField that defers database validation to the operations layer.

    Accepts raw primary key values without performing database lookups during
    validation. FK existence is validated in bulk in the list serializer pipeline.

    Contract:
    - queryset: must be a Django QuerySet or Manager (required unless read_only with model=).
    - If you have no natural queryset at field definition time, pass model=RelatedModel
      and omit queryset; we set queryset = model._default_manager.none() for DRF/schema introspection.
    """

    def __init__(self, *args, model=None, **kwargs):
        # Preserve model from both keyword arg and kwargs (fix overwrite bug)
        model = kwargs.pop("model", model)
        # DRF asserts queryset is not None unless read_only; when user passes only model=, inject queryset
        if kwargs.get("queryset", None) is None and model is not None:
            kwargs["queryset"] = model._default_manager.none()
        super().__init__(*args, **kwargs)
        self.queryset, _ = _normalize_queryset(self.queryset, model=model)

    def to_internal_value(self, data):
        """
        Accept and validate the raw PK value without database lookup.
        Database existence checking is done in the bulk serializer pipeline.
        """
        if data is None:
            if not self.allow_null:
                self.fail("required")
            return None

        try:
            model = self.queryset.model
            pk_type = model._meta.pk.get_internal_type()
            if pk_type in (
                "AutoField",
                "BigAutoField",
                "SmallAutoField",
                "IntegerField",
                "BigIntegerField",
            ):
                return int(data)
            return data
        except (ValueError, TypeError):
            self.fail("incorrect_type", data_type=type(data).__name__)


class BulkSlugRelatedField(SlugRelatedField):
    """
    A SlugRelatedField that defers database validation to the operations layer.

    Accepts raw slug values without performing database lookups during validation.
    Slug existence is validated in bulk in the list serializer pipeline.

    Contract:
    - queryset: must be a Django QuerySet or Manager (required unless read_only with model=).
    - If you have no natural queryset at field definition time, pass model=RelatedModel
      and omit queryset; we set queryset = model._default_manager.none() for DRF/schema introspection.
    """

    def __init__(self, *args, model=None, **kwargs):
        model = kwargs.pop("model", model)
        if kwargs.get("queryset", None) is None and model is not None:
            kwargs["queryset"] = model._default_manager.none()
        super().__init__(*args, **kwargs)
        self.queryset, _ = _normalize_queryset(self.queryset, model=model)

    def to_internal_value(self, data):
        """
        Accept and validate the raw slug value without database lookup.
        Database existence checking is done in the bulk serializer pipeline.
        """
        if data is None:
            if not self.allow_null:
                self.fail("required")
            return None

        data = str(data)
        if data == "":
            if not self.allow_blank:
                self.fail("blank")
            return ""
        return data


# Note: BulkHyperlinkedRelatedField is not provided; use BulkPrimaryKeyRelatedField for bulk.
