from __future__ import annotations

import enum
import types
import typing

from .context import RequestContext


class WorkspaceRole(enum.StrEnum):
    OWNER = 'owner'
    ADMIN = 'admin'
    DEVELOPER = 'developer'
    OPERATOR = 'operator'
    VIEWER = 'viewer'


class Permission(enum.StrEnum):
    WORKSPACE_VIEW = 'workspace.view'
    WORKSPACE_UPDATE = 'workspace.update'
    WORKSPACE_DELETE = 'workspace.delete'
    MEMBER_VIEW = 'member.view'
    MEMBER_INVITE = 'member.invite'
    MEMBER_UPDATE_ROLE = 'member.update_role'
    MEMBER_REMOVE = 'member.remove'
    RESOURCE_VIEW = 'resource.view'
    RESOURCE_MANAGE = 'resource.manage'
    RUNTIME_OPERATE = 'runtime.operate'
    PROVIDER_SECRET_MANAGE = 'provider_secret.manage'
    API_KEY_MANAGE = 'api_key.manage'
    AUDIT_VIEW = 'audit.view'
    DATA_EXPORT = 'data.export'
    BILLING_LINK_MANAGE = 'billing_link.manage'


_VIEW_PERMISSIONS = {
    Permission.WORKSPACE_VIEW,
    Permission.MEMBER_VIEW,
    Permission.RESOURCE_VIEW,
}

_ROLE_PERMISSIONS: typing.Final = types.MappingProxyType(
    {
        WorkspaceRole.OWNER: frozenset(Permission),
        WorkspaceRole.ADMIN: frozenset(
            permission
            for permission in Permission
            if permission
            not in {
                Permission.WORKSPACE_DELETE,
                Permission.BILLING_LINK_MANAGE,
            }
        ),
        WorkspaceRole.DEVELOPER: frozenset(
            _VIEW_PERMISSIONS
            | {
                Permission.RESOURCE_MANAGE,
                Permission.RUNTIME_OPERATE,
                Permission.PROVIDER_SECRET_MANAGE,
            }
        ),
        WorkspaceRole.OPERATOR: frozenset(_VIEW_PERMISSIONS | {Permission.RUNTIME_OPERATE}),
        WorkspaceRole.VIEWER: frozenset(_VIEW_PERMISSIONS),
    }
)


class AuthorizationError(Exception):
    """Base class for errors that map to an HTTP authorization response."""

    status_code = 403
    error_code = 'forbidden'


class AuthenticationDeniedError(AuthorizationError):
    status_code = 401
    error_code = 'invalid_authentication'


class WorkspaceRequiredError(AuthorizationError):
    status_code = 400
    error_code = 'workspace_required'


class PermissionDeniedError(AuthorizationError):
    error_code = 'permission_denied'

    def __init__(self, permission: str) -> None:
        super().__init__(f'Missing Workspace permission: {permission}')
        self.permission = permission


class EditionLimitError(AuthorizationError):
    error_code = 'edition_limit'


def permissions_for_role(role: str | WorkspaceRole) -> frozenset[str]:
    """Return the canonical fixed permissions for a Workspace role."""

    try:
        parsed_role = WorkspaceRole(role)
    except ValueError:
        return frozenset()
    return frozenset(permission.value for permission in _ROLE_PERMISSIONS[parsed_role])


def has_permission(ctx: RequestContext, permission: str | Permission) -> bool:
    """Return whether the context contains one effective permission."""

    permission_value = permission.value if isinstance(permission, Permission) else permission
    return permission_value in ctx.workspace.permissions


def require_permission(ctx: RequestContext, permission: str | Permission) -> None:
    """Raise a stable authorization error when a permission is missing."""

    permission_value = permission.value if isinstance(permission, Permission) else permission
    if not has_permission(ctx, permission_value):
        raise PermissionDeniedError(permission_value)
