"""
permissions.py
--------------
Permission enforcement logic for Memory Realms.
"""
from typing import Any, Dict, List, Set

from hieromem_core.realms.schema import MemoryRealm, PermissionType


class PermissionError(Exception):
    """Raised when an agent lacks required permissions."""
    pass


class CrossRealmAccessError(Exception):
    """Raised when cross-realm access is attempted without permission."""
    pass


def check_permission(realm: MemoryRealm, agent_id: str, required: PermissionType) -> bool:
    """
    Check if an agent has the required permission in a realm.

    Args:
        realm: The target MemoryRealm
        agent_id: The ID of the agent attempting access
        required: The permission required ('read', 'write', 'admin')

    Returns:
        True if permitted, False otherwise.
    """
    # Admins always have access
    perms = realm.get_permissions(agent_id)
    if "admin" in perms:
        return True

    return required in perms


def enforce_permission(realm: MemoryRealm, agent_id: str, required: PermissionType) -> None:
    """
    Enforce permission, raising PermissionError if failed.
    """
    if not check_permission(realm, agent_id, required):
        raise PermissionError(
            f"Agent '{agent_id}' lacks '{required}' permission for realm '{realm.name}'"
        )


def get_accessible_realms(
    realms: Dict[str, MemoryRealm],
    agent_id: str,
    required: PermissionType = "read",
) -> List[str]:
    """
    Get all realms an agent can access with the required permission.

    Args:
        realms: Dictionary of realm name to MemoryRealm
        agent_id: The agent ID
        required: The permission level required

    Returns:
        List of realm names the agent can access
    """
    accessible = []
    for name, realm in realms.items():
        if check_permission(realm, agent_id, required):
            accessible.append(name)
    return accessible


def validate_cross_realm_access(
    source_realm: MemoryRealm,
    target_realm: MemoryRealm,
    agent_id: str,
) -> bool:
    """
    Validate if an agent can create cross-realm references.

    Cross-realm references require:
    - Read permission on source realm
    - Write permission on target realm
    - Both realms must be 'shared' or 'hybrid' type

    Args:
        source_realm: The source realm
        target_realm: The target realm
        agent_id: The agent attempting the cross-reference

    Returns:
        True if cross-realm access is allowed
    """
    # Check realm types allow cross-references
    if source_realm.type == "private" or target_realm.type == "private":
        return False

    # Check agent has required permissions
    if not check_permission(source_realm, agent_id, "read"):
        return False
    if not check_permission(target_realm, agent_id, "write"):
        return False

    return True


def enforce_cross_realm_access(
    source_realm: MemoryRealm,
    target_realm: MemoryRealm,
    agent_id: str,
) -> None:
    """
    Enforce cross-realm access rules, raising CrossRealmAccessError if denied.
    """
    if not validate_cross_realm_access(source_realm, target_realm, agent_id):
        raise CrossRealmAccessError(
            f"Agent '{agent_id}' cannot create cross-reference from "
            f"realm '{source_realm.name}' to realm '{target_realm.name}'"
        )


def get_realm_isolation_level(realm: MemoryRealm) -> str:
    """
    Get the isolation level of a realm.

    Args:
        realm: The MemoryRealm to check

    Returns:
        'logical' or 'cryptographic'
    """
    return realm.isolation_level


def filter_episodes_by_realm(
    episodes: List[Any],
    allowed_namespaces: Set[str],
) -> List[Any]:
    """
    Filter episodes to only include those from allowed namespaces/realms.

    Args:
        episodes: List of episodes to filter
        allowed_namespaces: Set of namespace/realm names the agent can access

    Returns:
        Filtered list of episodes
    """
    filtered = []
    for ep in episodes:
        namespace = getattr(ep, 'namespace', None)
        if namespace is None:
            meta = getattr(ep, 'meta', {})
            namespace = meta.get('namespace') if meta else None
        if namespace is None or namespace in allowed_namespaces:
            filtered.append(ep)
    return filtered
