# retoor <retoor@molodetz.nl>

from __future__ import annotations

import re
from dataclasses import dataclass

from devplacepy.database import get_setting, internal_gateway_key
from devplacepy.utils import (
    _user_from_api_key,
    get_current_user,
    is_admin,
)

TOPIC_NAME = re.compile(r"^[A-Za-z0-9_.*-]{1,128}$")
MAX_PAYLOAD_BYTES = 64 * 1024


@dataclass(frozen=True)
class Actor:
    kind: str
    uid: str
    username: str

    @property
    def privileged(self) -> bool:
        return self.kind in ("admin", "internal")


def guests_enabled() -> bool:
    return get_setting("pubsub_allow_guests", "0") == "1"


def _bearer_key(scope) -> str:
    key = scope.headers.get("x-api-key", "").strip()
    if key:
        return key
    scheme, _, credentials = scope.headers.get("authorization", "").partition(" ")
    if scheme.lower() == "bearer":
        return credentials.strip()
    return ""


def resolve_actor(scope) -> Actor:
    user = get_current_user(scope)
    if user and is_admin(user):
        return Actor(kind="admin", uid=user["uid"], username=user.get("username", ""))
    key = _bearer_key(scope)
    if key and key == internal_gateway_key():
        return Actor(kind="internal", uid="internal", username="internal")
    if key:
        keyed = _user_from_api_key(key)
        if keyed and is_admin(keyed):
            return Actor(
                kind="admin", uid=keyed["uid"], username=keyed.get("username", "")
            )
    if user:
        return Actor(kind="user", uid=user["uid"], username=user.get("username", ""))
    return Actor(kind="guest", uid="", username="guest")


def valid_topic(topic: str) -> bool:
    return bool(topic and TOPIC_NAME.match(topic))


def _own_namespace(actor: Actor, topic: str) -> bool:
    if not actor.uid:
        return False
    base = f"user.{actor.uid}"
    return topic == base or topic.startswith(base + ".")


def _is_public(topic: str) -> bool:
    return topic == "public" or topic.startswith("public.") or topic.startswith("public")


def can_subscribe(actor: Actor, topic: str) -> bool:
    if actor.privileged:
        return True
    if topic == "*":
        return False
    if topic == "public" or topic.startswith("public."):
        return actor.kind != "guest" or guests_enabled()
    if actor.kind == "user" and _own_namespace(actor, topic):
        return True
    return False


def can_publish(actor: Actor, topic: str) -> bool:
    if actor.privileged:
        return True
    if actor.kind == "guest":
        return False
    if actor.kind == "user" and _own_namespace(actor, topic):
        return True
    return False
