"""KB read-only query layer (selectors).

All read operations should go through this module. Selectors return QuerySets
or model instances and raise DoesNotExist on miss.
"""

from __future__ import annotations

from django.db.models import QuerySet

from simorgh.apps.kb.models import (
    ArticleStatus,
    ArticleVisibility,
    KBArticle,
    KBCategory,
)


# ---------------------------------------------------------------------------
# Category selectors
# ---------------------------------------------------------------------------

def categories_for_tenant(tenant_id: int) -> QuerySet[KBCategory]:
    return KBCategory.objects.filter(tenant_id=tenant_id).select_related("parent")


def category_by_id(tenant_id: int, pk: int) -> KBCategory:
    return KBCategory.objects.get(tenant_id=tenant_id, pk=pk)


# ---------------------------------------------------------------------------
# Article selectors
# ---------------------------------------------------------------------------

def articles_for_tenant(
    tenant_id: int,
    *,
    status: str | None = None,
    visibility: str | None = None,
    category_id: int | None = None,
    author_id: int | None = None,
) -> QuerySet[KBArticle]:
    qs = (
        KBArticle.objects.filter(tenant_id=tenant_id)
        .select_related("category", "author")
        .order_by("-created_at")
    )
    if status is not None:
        qs = qs.filter(status=status)
    if visibility is not None:
        qs = qs.filter(visibility=visibility)
    if category_id is not None:
        qs = qs.filter(category_id=category_id)
    if author_id is not None:
        qs = qs.filter(author_id=author_id)
    return qs


def articles_visible_to(
    tenant_id: int,
    *,
    user_id: int | None = None,
    is_authenticated: bool = False,
) -> QuerySet[KBArticle]:
    """Filter articles to only those the caller may see.

    * public     → all callers (including anonymous)
    * internal   → authenticated users
    * private    → the author only
    """
    qs = KBArticle.objects.filter(
        tenant_id=tenant_id,
        status=ArticleStatus.PUBLISHED,
    ).select_related("category", "author")

    if user_id is not None:
        # Authenticated: can see public + internal + their own private articles
        from django.db.models import Q
        qs = qs.filter(
            Q(visibility=ArticleVisibility.PUBLIC)
            | Q(visibility=ArticleVisibility.INTERNAL)
            | Q(visibility=ArticleVisibility.PRIVATE, author_id=user_id)
        )
    elif is_authenticated:
        from django.db.models import Q
        qs = qs.filter(
            Q(visibility=ArticleVisibility.PUBLIC)
            | Q(visibility=ArticleVisibility.INTERNAL)
        )
    else:
        qs = qs.filter(visibility=ArticleVisibility.PUBLIC)

    return qs.order_by("-published_at")


def article_by_public_id(tenant_id: int, public_id: str) -> KBArticle:
    return KBArticle.objects.select_related("category", "author").get(
        tenant_id=tenant_id, public_id=public_id
    )


def article_by_slug(tenant_id: int, slug: str) -> KBArticle:
    """Return only published + public articles by slug (for public portal)."""
    return KBArticle.objects.select_related("category", "author").get(
        tenant_id=tenant_id,
        slug=slug,
        status=ArticleStatus.PUBLISHED,
        visibility=ArticleVisibility.PUBLIC,
    )
