import functools
from collections.abc import Iterable

from pyidentity.exc import ArgumentNullError
from pyidentity.security.authentication.abc import IAuthenticationSchemeProvider
from pyidentity.security.authentication.options import AuthenticationOptions
from pyidentity.security.authentication.scheme import AuthenticationScheme

__all__ = ("AuthenticationSchemeProvider",)


class AuthenticationSchemeProvider(IAuthenticationSchemeProvider):
    """Implements *IAuthenticationSchemeProvider*."""

    __slots__ = ("_options",)

    def __init__(self, options: AuthenticationOptions) -> None:
        self._options = options

    @functools.cache
    def get_all_schemes(self) -> Iterable[AuthenticationScheme]:
        return self._options.scheme_map.values()

    @functools.cache
    def get_scheme(self, name: str) -> AuthenticationScheme | None:
        if name is None:
            raise ArgumentNullError("name")
        return self._options.scheme_map.get(name)

    @functools.cache
    def get_default_authentication_scheme(self) -> AuthenticationScheme | None:
        if name := self._options.default_authentication_scheme:
            return self.get_scheme(name)
        return self.get_default_scheme()

    @functools.cache
    def get_default_sign_in_scheme(self) -> AuthenticationScheme | None:
        if name := self._options.default_sign_in_scheme:
            return self.get_scheme(name)
        return self.get_default_scheme()

    @functools.cache
    def get_default_sign_out_scheme(self) -> AuthenticationScheme | None:
        if name := self._options.default_sign_out_scheme:
            return self.get_scheme(name)
        return self.get_default_sign_in_scheme()

    @functools.cache
    def get_default_scheme(self) -> AuthenticationScheme | None:
        if name := self._options.default_scheme:
            return self.get_scheme(name)
        # Return the first of the schemes
        return next(s for s in self._options.scheme_map.values())
