from typing import Any, TypeAlias, TYPE_CHECKING
from django.db.models import QuerySet
from rest_framework.serializers import Field, ModelSerializer

try:
    from rest_framework.validators import UniqueValidator, record_already_exists
    from rest_framework.exceptions import ValidationError
except ImportError:
    raise SystemExit(
        "Unable to import `record_already_exists`. "
        "Ensure that the Compass fork of Django Rest Framework is installed!"
    )
if TYPE_CHECKING:
    from console_base.models import BaseIntegerPKModel

    class PreCreateValidationBase(ModelSerializer):
        class Meta:
            fields: tuple[str, ...]
            model: type[BaseIntegerPKModel]

else:
    PreCreateValidationBase = object


# ---------------------------------------------------------------------------
class LCUnique(UniqueValidator):
    def __init__(
        self,
        queryset: QuerySet,
        value: Any,
        field: str = 'cid',
        message: str | None = None,
        lookup: str = 'exact',
    ) -> None:
        super().__init__(queryset=queryset, message=message, lookup=lookup)

        self.field_name = field

    def __call__(self, value: Any, serializer_field: Field | None = None) -> None:
        queryset = self.filter_queryset(value, self.queryset, self.field_name)
        if existing_record_cid := queryset.values_list('cid', flat=True).first():
            raise ValidationError(
                self.message,
                code='unique',
                existing_record_cid=existing_record_cid,
            )


# ---------------------------------------------------------------------------
class PreCreateValidation(PreCreateValidationBase):
    """
    Any validation that should only take place on 'create'.
    such as checking for unique values, because we so often
    override the field-level UniqueValidator on nested serializers
    """

    def pre_create_validation(self, data: dict) -> dict:
        for field in ('cid', 'pk', 'id'):
            if field not in data:
                continue
            LCUnique(
                queryset=self.Meta.model.objects.all(),
                value=data[field],
                field=field,
            )
        return data


__all__ = (
    'LCUnique',
    'PreCreateValidation',
)
