import django_filters
from django import forms
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models

from rest_framework import serializers

from . import search

PRIVACY_LEVEL_CHOICES = [
    ("me", "Only me"),
    ("followers", "Me and my followers"),
    ("instance", "Everyone on my instance, and my followers"),
    ("everyone", "Everyone, including people on other instances"),
]


def get_privacy_field():
    return models.CharField(
        max_length=30, choices=PRIVACY_LEVEL_CHOICES, default="instance"
    )


def privacy_level_query(user, lookup_field="privacy_level", user_field="user"):
    if user.is_anonymous:
        return models.Q(**{lookup_field: "everyone"})

    return models.Q(
        **{"{}__in".format(lookup_field): ["instance", "everyone"]}
    ) | models.Q(**{lookup_field: "me", user_field: user})


class SearchFilter(django_filters.CharFilter):
    def __init__(self, *args, **kwargs):
        self.search_fields = kwargs.pop("search_fields")
        super().__init__(*args, **kwargs)

    def filter(self, qs, value):
        if not value:
            return qs
        query = search.get_query(value, self.search_fields)
        return qs.filter(query)


class SmartSearchFilter(django_filters.CharFilter):
    def __init__(self, *args, **kwargs):
        self.config = kwargs.pop("config")
        super().__init__(*args, **kwargs)

    def filter(self, qs, value):
        if not value:
            return qs
        try:
            cleaned = self.config.clean(value)
        except (forms.ValidationError):
            return qs.none()
        return search.apply(qs, cleaned)


class GenericRelation(serializers.JSONField):
    def __init__(self, choices, *args, **kwargs):
        self.choices = choices
        self.encoder = kwargs.setdefault("encoder", DjangoJSONEncoder)
        super().__init__(*args, **kwargs)

    def to_representation(self, value):
        if not value:
            return
        type = None
        id = None
        for key, choice in self.choices.items():
            if isinstance(value, choice["queryset"].model):
                type = key
                id = getattr(value, choice.get("id_attr", "id"))
                break

        if type:
            return {"type": type, "id": id}

    def to_internal_value(self, v):
        v = super().to_internal_value(v)

        if not v or not isinstance(v, dict):
            raise serializers.ValidationError("Invalid data")

        try:
            type = v["type"]
            field = serializers.ChoiceField(choices=list(self.choices.keys()))
            type = field.to_internal_value(type)
        except (TypeError, KeyError, serializers.ValidationError):
            raise serializers.ValidationError("Invalid type")

        conf = self.choices[type]
        id_attr = conf.get("id_attr", "id")
        id_field = conf.get("id_field", serializers.IntegerField(min_value=1))
        queryset = conf["queryset"]
        try:
            id_value = v[id_attr]
            id_value = id_field.to_internal_value(id_value)
        except (TypeError, KeyError, serializers.ValidationError):
            raise serializers.ValidationError("Invalid {}".format(id_attr))

        query_getter = conf.get(
            "get_query", lambda attr, value: models.Q(**{attr: value})
        )
        query = query_getter(id_attr, id_value)
        try:
            obj = queryset.get(query)
        except queryset.model.DoesNotExist:
            raise serializers.ValidationError("Object not found")

        return obj