from pathlib import Path
from typing import Annotated

from annotated_types import MinLen
from django.contrib.staticfiles.storage import staticfiles_storage
from django.db.models import Q
from django.utils.text import slugify
from haystack.query import SearchQuerySet
from ninja import FilterSchema, ModelSchema, Schema
from pydantic import AliasChoices, Field

from core.models import Group, SithFile, User


class SimpleUserSchema(ModelSchema):
    """A schema with the minimum amount of information to represent a user."""

    class Meta:
        model = User
        fields = ["id", "nick_name", "first_name", "last_name"]


class UserProfileSchema(ModelSchema):
    """The necessary information to show a user profile"""

    class Meta:
        model = User
        fields = ["id", "nick_name", "first_name", "last_name"]

    display_name: str
    profile_url: str
    profile_pict: str

    @staticmethod
    def resolve_display_name(obj: User) -> str:
        return obj.get_display_name()

    @staticmethod
    def resolve_profile_url(obj: User) -> str:
        return obj.get_absolute_url()

    @staticmethod
    def resolve_profile_pict(obj: User) -> str:
        if obj.profile_pict_id is None:
            return staticfiles_storage.url("core/img/unknown.jpg")
        return obj.profile_pict.get_download_url()


class SithFileSchema(ModelSchema):
    class Meta:
        model = SithFile
        fields = ["id", "name"]

    path: str

    @staticmethod
    def resolve_path(obj: SithFile) -> str:
        return str(Path(obj.get_parent_path()) / obj.name)


class GroupSchema(ModelSchema):
    class Meta:
        model = Group
        fields = ["id", "name"]


class UserFilterSchema(FilterSchema):
    search: Annotated[str, MinLen(1)]
    exclude: list[int] | None = Field(
        None, validation_alias=AliasChoices("exclude", "exclude[]")
    )

    def filter_search(self, value: str | None) -> Q:
        if not value:
            return Q()
        if len(value) < 3:
            # For small queries, full text search isn't necessary
            return (
                Q(first_name__istartswith=value)
                | Q(last_name__istartswith=value)
                | Q(nick_name__istartswith=value)
            )
        return Q(
            id__in=list(
                SearchQuerySet()
                .models(User)
                .autocomplete(auto=slugify(value).replace("-", " "))
                .values_list("pk", flat=True)
            )
        )

    def filter_exclude(self, value: set[int] | None) -> Q:
        if not value:
            return Q()
        return ~Q(id__in=value)


class MarkdownSchema(Schema):
    text: str


class FamilyGodfatherSchema(Schema):
    godfather: int
    godchild: int


class UserFamilySchema(Schema):
    """Represent a graph of a user's family"""

    users: list[UserProfileSchema]
    relationships: list[FamilyGodfatherSchema]
