"""DMS versioning — query layer.

All DB reads for DocumentLock, CheckoutSession, and VersionDiff.
"""

from __future__ import annotations

import uuid

from django.db.models import QuerySet
from django.utils import timezone

from simorgh.apps.dms.common.exceptions import AssetNotFound
from simorgh.apps.dms.versioning.models import CheckoutSession, DocumentLock, VersionDiff


# ---------------------------------------------------------------------------
# DocumentLock queries
# ---------------------------------------------------------------------------

def get_active_lock(document) -> DocumentLock | None:
    """Return the single active lock for *document*, or None."""
    return (
        DocumentLock.objects.filter(document=document, is_active=True, is_deleted=False)
        .select_related("locked_by")
        .first()
    )


def get_lock(tenant_id: int, public_id: str) -> DocumentLock:
    """Retrieve a lock by public_id within a tenant.  Raises AssetNotFound."""
    try:
        uid = uuid.UUID(str(public_id))
    except (ValueError, AttributeError) as exc:
        raise AssetNotFound(f"Invalid lock id: {public_id!r}") from exc
    try:
        return DocumentLock.objects.select_related("document", "locked_by").get(
            tenant_id=tenant_id, public_id=uid, is_deleted=False
        )
    except DocumentLock.DoesNotExist as exc:
        raise AssetNotFound(f"DocumentLock {public_id!r} not found.") from exc


def list_locks_for_document(document) -> QuerySet:
    """All locks (active and inactive) for a document, newest first."""
    return (
        DocumentLock.objects.filter(document=document, is_deleted=False)
        .select_related("locked_by")
        .order_by("-created_at")
    )


def get_expired_active_locks() -> QuerySet:
    """Active locks that have passed their expiry time — for the scheduler."""
    return DocumentLock.objects.filter(
        is_active=True,
        is_deleted=False,
        expires_at__isnull=False,
        expires_at__lt=timezone.now(),
    ).select_related("document", "locked_by")


# ---------------------------------------------------------------------------
# CheckoutSession queries
# ---------------------------------------------------------------------------

def list_checkout_history(document) -> QuerySet:
    """Ordered audit log of all checkout events for a document."""
    return (
        CheckoutSession.objects.filter(document=document)
        .select_related("actor", "lock_holder", "version_created")
        .order_by("-created_at")
    )


# ---------------------------------------------------------------------------
# VersionDiff queries
# ---------------------------------------------------------------------------

def get_version_diff(tenant_id: int, public_id: str) -> VersionDiff:
    try:
        uid = uuid.UUID(str(public_id))
    except (ValueError, AttributeError) as exc:
        raise AssetNotFound(f"Invalid diff id: {public_id!r}") from exc
    try:
        return VersionDiff.objects.select_related("from_version", "to_version").get(
            tenant_id=tenant_id, public_id=uid, is_deleted=False
        )
    except VersionDiff.DoesNotExist as exc:
        raise AssetNotFound(f"VersionDiff {public_id!r} not found.") from exc


def get_version_diff_for_versions(
    from_version, to_version, diff_type: str
) -> VersionDiff | None:
    """Return an existing non-deleted diff for the given version pair + type."""
    return VersionDiff.objects.filter(
        from_version=from_version,
        to_version=to_version,
        diff_type=diff_type,
        is_deleted=False,
    ).first()


def list_version_diffs_for_document(document) -> QuerySet:
    """All non-deleted diffs whose source version belongs to *document*."""
    return (
        VersionDiff.objects.filter(
            from_version__document=document,
            is_deleted=False,
        )
        .select_related("from_version", "to_version")
        .order_by("-created_at")
    )
