"""Permission and role system for Remote Access API."""

from typing import Dict, FrozenSet, List, Optional, Tuple

import structlog

from .models import UserRole

logger = structlog.get_logger(__name__)


class PermissionChecker:
    """User permission checker class."""

    ROLE_MAPPING: Dict[str, UserRole] = {
        "ADMINISTER": UserRole.SUPER_ADMIN,
        "CREATE_USER": UserRole.ADMIN,
        "CREATE_CONNECTION": UserRole.USER,
    }

    ROLE_PERMISSIONS: Dict[UserRole, FrozenSet[str]] = {
        UserRole.GUEST: frozenset({
            "view_own_connections",
            "view_own_profile"
        }),
        UserRole.USER: frozenset({
            "view_own_connections",
            "view_own_profile",
            "create_connections",
            "delete_own_connections",
        }),
        UserRole.ADMIN: frozenset({
            "view_own_connections",
            "view_own_profile",
            "create_connections",
            "delete_own_connections",
            "view_all_connections",
            "delete_any_connection",
            "view_system_stats",
            "view_system_metrics",
        }),
        UserRole.SUPER_ADMIN: frozenset({
            "view_own_connections",
            "view_own_profile",
            "create_connections",
            "delete_own_connections",
            "view_all_connections",
            "delete_any_connection",
            "view_system_stats",
            "view_system_metrics",
            "reset_system_stats",
            "manage_users",
            "view_system_logs",
            "change_system_config",
        }),
    }

    ENDPOINT_PERMISSIONS = {
        "POST /connect": "create_connections",
        "GET /connections": "view_own_connections",
        "DELETE /connections": "delete_own_connections",
        "GET /stats": "view_system_stats",
        "GET /metrics": "view_system_metrics",
        "POST /stats/reset": "reset_system_stats",
        "GET /auth/profile": "view_own_profile",
        "GET /auth/permissions": "view_own_profile",
    }
    
    @classmethod
    def determine_role_from_permissions(cls, guacamole_permissions: List[str]) -> UserRole:
        """
        Determine user role based on Guacamole system permissions.

        Args:
            guacamole_permissions: List of system permissions from Guacamole.

        Returns:
            User role.
        """
        for permission, role in cls.ROLE_MAPPING.items():
            if permission in guacamole_permissions:
                logger.debug(
                    "Role determined from permission",
                    permission=permission,
                    role=role.value,
                    all_permissions=guacamole_permissions,
                )
                return role

        logger.debug(
            "No system permissions found, assigning GUEST role",
            permissions=guacamole_permissions,
        )
        return UserRole.GUEST
    
    @classmethod
    def get_role_permissions(cls, role: UserRole) -> FrozenSet[str]:
        """
        Get all permissions for a role.

        Args:
            role: User role.

        Returns:
            Frozen set of permissions.
        """
        return cls.ROLE_PERMISSIONS.get(role, frozenset())

    @classmethod
    def check_permission(cls, user_role: UserRole, permission: str) -> bool:
        """
        Check if role has specific permission.

        Args:
            user_role: User role.
            permission: Permission to check.

        Returns:
            True if permission exists, False otherwise.
        """
        role_permissions = cls.get_role_permissions(user_role)
        has_permission = permission in role_permissions

        logger.debug(
            "Permission check",
            role=user_role.value,
            permission=permission,
            allowed=has_permission,
        )

        return has_permission
    
    @classmethod
    def check_endpoint_access(
        cls, user_role: UserRole, method: str, path: str
    ) -> Tuple[bool, Optional[str]]:
        """
        Check endpoint access.

        Args:
            user_role: User role.
            method: HTTP method (GET, POST, DELETE, etc.).
            path: Endpoint path.

        Returns:
            Tuple of (allowed: bool, reason: Optional[str]).
        """
        endpoint_key = f"{method} {path}"

        required_permission = cls.ENDPOINT_PERMISSIONS.get(endpoint_key)

        if not required_permission:
            for pattern, permission in cls.ENDPOINT_PERMISSIONS.items():
                if cls._match_endpoint_pattern(endpoint_key, pattern):
                    required_permission = permission
                    break

        if not required_permission:
            return True, None

        has_permission = cls.check_permission(user_role, required_permission)

        if not has_permission:
            reason = (
                f"Required permission '{required_permission}' "
                f"not granted to role '{user_role.value}'"
            )
            logger.info(
                "Endpoint access denied",
                role=user_role.value,
                endpoint=endpoint_key,
                required_permission=required_permission,
                reason=reason,
            )
            return False, reason

        logger.debug(
            "Endpoint access granted",
            role=user_role.value,
            endpoint=endpoint_key,
            required_permission=required_permission,
        )

        return True, None
    
    @classmethod
    def _match_endpoint_pattern(cls, endpoint: str, pattern: str) -> bool:
        """
        Check if endpoint matches pattern.

        Args:
            endpoint: Endpoint to check (e.g., "DELETE /connections/123").
            pattern: Pattern (e.g., "DELETE /connections").

        Returns:
            True if matches.
        """
        if pattern.endswith("/connections"):
            base_pattern = pattern.replace("/connections", "/connections/")
            return endpoint.startswith(base_pattern)

        return False
    
    @classmethod
    def check_connection_ownership(
        cls, user_role: UserRole, username: str, connection_owner: str
    ) -> Tuple[bool, Optional[str]]:
        """
        Check connection management rights.

        Args:
            user_role: User role.
            username: Username.
            connection_owner: Connection owner.

        Returns:
            Tuple of (allowed: bool, reason: Optional[str]).
        """
        if user_role in (UserRole.ADMIN, UserRole.SUPER_ADMIN):
            return True, None

        if username == connection_owner:
            return True, None

        reason = (
            f"User '{username}' cannot manage connection owned by '{connection_owner}'"
        )
        logger.info(
            "Connection ownership check failed",
            user=username,
            owner=connection_owner,
            role=user_role.value,
            reason=reason,
        )

        return False, reason
    
    @classmethod
    def can_view_all_connections(cls, user_role: UserRole) -> bool:
        """
        Check if user can view all connections.

        Args:
            user_role: User role.

        Returns:
            True if can view all connections.
        """
        return cls.check_permission(user_role, "view_all_connections")

    @classmethod
    def can_delete_any_connection(cls, user_role: UserRole) -> bool:
        """
        Check if user can delete any connection.

        Args:
            user_role: User role.

        Returns:
            True if can delete any connection.
        """
        return cls.check_permission(user_role, "delete_any_connection")

    @classmethod
    def get_user_permissions_list(cls, user_role: UserRole) -> List[str]:
        """
        Get sorted list of user permissions for API response.

        Args:
            user_role: User role.

        Returns:
            Sorted list of permissions.
        """
        permissions = cls.get_role_permissions(user_role)
        return sorted(permissions)

    @classmethod
    def validate_role_hierarchy(
        cls, current_user_role: UserRole, target_user_role: UserRole
    ) -> bool:
        """
        Validate role hierarchy for user management.

        Args:
            current_user_role: Current user role.
            target_user_role: Target user role.

        Returns:
            True if current user can manage target user.
        """
        return current_user_role == UserRole.SUPER_ADMIN 