from types import MappingProxyType
from typing import overload

from pyidentity.exc import ArgumentNullError, InvalidOperationError
from pyidentity.security.authorization.policy import AuthorizationPolicy
from pyidentity.security.authorization.policy_builder import AuthorizationPolicyBuilder
from pyidentity.security.authorization.types import ConfigurePolicy

__all__ = ("AuthorizationOptions",)


class AuthorizationOptions:
    """Provides programmatic configuration used by *IAuthorizationPolicyProvider*."""

    __slots__ = ("__policy_map", "default_policy")

    def __init__(self) -> None:
        self.__policy_map: dict[str, AuthorizationPolicy] = {}
        self.default_policy = AuthorizationPolicyBuilder().require_authenticated_user().build()
        """Gets or sets the default authorization policy. Defaults to require authenticated users."""

    @property
    def policy_map(self) -> MappingProxyType[str, AuthorizationPolicy]:
        """Maps polices by name."""
        return MappingProxyType(self.__policy_map)

    @overload
    def add_policy(self, name: str, policy: AuthorizationPolicy, /) -> None: ...

    @overload
    def add_policy(self, name: str, configure_builder: ConfigurePolicy, /) -> None: ...

    def add_policy(
        self,
        name: str,
        policy_or_configure_builder: AuthorizationPolicy | ConfigurePolicy,
        /,
    ) -> None:
        """Add an authorization policy with the provided name or built from a delegate with the provided name.

        :param name: The name of the policy.
        :param policy_or_configure_builder: The authorization policy or delegate that will be used to build the policy.
        :return:
        """
        if name is None:
            raise ArgumentNullError("name")
        if policy_or_configure_builder is None:
            raise ArgumentNullError("policy_or_configure_builder")

        if isinstance(policy_or_configure_builder, AuthorizationPolicy):
            policy = policy_or_configure_builder
        elif callable(policy_or_configure_builder):
            builder = AuthorizationPolicyBuilder()
            policy_or_configure_builder(builder)
            policy = builder.build()
        else:
            raise InvalidOperationError("Unsupported function arguments.")

        self.__policy_map[name] = policy

    def get_policy(self, name: str) -> AuthorizationPolicy | None:
        """Returns the policy for the specified name, or null if a policy with the name does not exist.

        :param name: The name of the policy to return.
        :return: The policy for the specified name, or null if a policy with the name does not exist.
        """
        if name is None:
            raise ArgumentNullError("name")
        return self.__policy_map.get(name)
