from __future__ import annotations

from dataclasses import dataclass

from sqlalchemy import func, or_
from sqlalchemy.orm import Session

from govoplan_core.core.change_sequence import (
    ChangeSequenceEntry,
    retained_sequence_floor,
    record_change,
)

AUTH_PRINCIPAL_REVISION_COLLECTION = "core.auth-principal-revisions"


@dataclass(frozen=True, slots=True)
class AuthPrincipalRevision:
    system: int
    tenant: int


def auth_principal_revision(
    session: Session,
    *,
    tenant_id: str | None,
) -> AuthPrincipalRevision:
    """Return the durable global and tenant authorization revision.

    The sequence rows make invalidation visible across processes. Retention
    floors keep revisions monotonic if old change rows are pruned.
    """

    rows = (
        session.query(
            ChangeSequenceEntry.tenant_id,
            func.max(ChangeSequenceEntry.id),
        )
        .filter(
            ChangeSequenceEntry.module_id == "core",
            ChangeSequenceEntry.collection == AUTH_PRINCIPAL_REVISION_COLLECTION,
            or_(
                ChangeSequenceEntry.tenant_id.is_(None),
                ChangeSequenceEntry.tenant_id == tenant_id,
            ),
        )
        .group_by(ChangeSequenceEntry.tenant_id)
        .all()
    )
    revisions = {row_tenant_id: int(sequence_id or 0) for row_tenant_id, sequence_id in rows}
    system = max(
        revisions.get(None, 0),
        retained_sequence_floor(
            session,
            tenant_id=None,
            module_id="core",
            collections=(AUTH_PRINCIPAL_REVISION_COLLECTION,),
        ),
    )
    tenant = 0
    if tenant_id is not None:
        tenant = max(
            revisions.get(tenant_id, 0),
            retained_sequence_floor(
                session,
                tenant_id=tenant_id,
                module_id="core",
                collections=(AUTH_PRINCIPAL_REVISION_COLLECTION,),
            ),
        )
    return AuthPrincipalRevision(system=system, tenant=tenant)


def invalidate_auth_principals(
    session: Session,
    *,
    tenant_id: str | None,
    source_module: str,
    resource_type: str,
    resource_id: str,
    actor_type: str | None = None,
    actor_id: str | None = None,
    reason: str | None = None,
) -> ChangeSequenceEntry:
    """Advance the authorization revision in the caller's transaction."""

    return record_change(
        session,
        tenant_id=tenant_id,
        module_id="core",
        collection=AUTH_PRINCIPAL_REVISION_COLLECTION,
        resource_type="auth_principal_revision",
        resource_id=tenant_id or "system",
        operation="invalidated",
        actor_type=actor_type,
        actor_id=actor_id,
        payload={
            "source_module": source_module,
            "resource_type": resource_type,
            "resource_id": resource_id,
            **({"reason": reason} if reason else {}),
        },
    )


__all__ = [
    "AUTH_PRINCIPAL_REVISION_COLLECTION",
    "AuthPrincipalRevision",
    "auth_principal_revision",
    "invalidate_auth_principals",
]
