# Path: esteid/exceptions.py
# class ActionInProgress(EsteidError):
#     """
#     Raised when a request to the service is still in pending status,
#     or when it is necessary to move on to the next step while passing some data on to the user
#     (like after successful auth, we initiate a signing request and display verification code)
#     """
# 
#     status = HTTPStatus.ACCEPTED
# 
#     default_message = _("Operation is in progress.")
# 
#     def __init__(self, message=None, data: dict = None):
#         super().__init__(message)
#         self.data = data or {}
# 
# class BadRequest(EsteidError):
#     pass
# 
# class EsteidError(Exception):
#     """
#     A generic Esteid error.
# 
#     Provides an interface for displaying a translated message to the user.
# 
#     Note: by default, any message passed as a first positional argument is ignored, because it only
#     serves for better logging. The User Interface should display a verbose error message
#     in case of a user-caused error so that user can take action accordingly. Errors caused by service malfunction
#     are likely not informative to a user without malicious intent.
# 
#     If the default message contains {key} placeholders, the exception is supposed to be
#     raised with kwargs, these will be passed on to <translated default message>.format(**kwargs).
#     """
# 
#     status = HTTPStatus.INTERNAL_SERVER_ERROR
# 
#     default_message = _("Something went wrong")
# 
#     kwargs: dict
# 
#     def __init__(self, message=None, **kwargs):
#         super().__init__(message)
#         self.kwargs = kwargs
# 
#     def get_message(self):
#         return str(self.default_message).format(**self.kwargs)
# 
#     def get_user_error(self):
#         return {
#             "error": self.__class__.__name__,  # error code that can be bound to JS translations
#             # TODO review errors and add translations
#             "message": self.get_message(),
#         }
# 
# class InvalidCredentials(EsteidError):
#     pass
# 
# class OfflineError(UpstreamServiceError):
#     default_message = _("The {service} service is unavailable at the moment. Please try again later.")
# 
# class SessionDoesNotExist(EsteidError):
#     pass
# 
# class UnsupportedClientImplementation(UpstreamServiceError):
#     pass
# 
# class UpstreamServiceError(EsteidError):
#     """A failure reported by upstream service which is not caused by user.
# 
#     Takes a `service` kwarg, e.g. Mobile ID / Smart ID
#     """
# 
#     status = HTTPStatus.SERVICE_UNAVAILABLE
# 
#     default_message = _("The {service} service reported a failure.")
# 
#     def __init__(self, message=None, *, service, **kwargs):
#         super().__init__(message)
#         self.kwargs = {"service": service, **kwargs}


import logging
import requests
from typing import Union
from uuid import UUID
from .exceptions import (
    ActionInProgress,
    BadRequest,
    EsteidError,
    InvalidCredentials,
    OfflineError,
    SessionDoesNotExist,
    UnsupportedClientImplementation,
    UpstreamServiceError,
)
        try:
            return response.json()
        except ValueError as e:  # pragma: no cover
            logger.exception("Failed to parse %s response as JSON:\n%s", req.url, response.content)
            raise UpstreamServiceError("Failed to parse response as JSON", service=self.NAME) from e

    def poll_session(self, session_id, endpoint_url, timeout=None) -> dict:
        """
        Polls session status with a specified timeout and returns the received result.

        The timeout defaults to 500ms to avoid extremely long blocking. If it is done asynchronously via Celery,
        it can be increased as per the docs below.

        https://github.com/SK-EID/MID#334-long-polling
        https://github.com/SK-EID/smart-id-documentation#46-session-status
        """
        endpoint_url = endpoint_url.format(session_id=session_id)
        try:
            data = self.invoke(
                endpoint_url,
                query={
                    "timeoutMs": timeout or 500,
                },
            )

        except UpstreamServiceError as e:
            cause = getattr(e, "__cause__", None)
            if isinstance(cause, requests.HTTPError):
                response = cause.response  # pylint: disable=no-member
                if response.status_code == 404:

# Path: esteid/exceptions.py
# class ActionInProgress(EsteidError):
#     """
#     Raised when a request to the service is still in pending status,
#     or when it is necessary to move on to the next step while passing some data on to the user
#     (like after successful auth, we initiate a signing request and display verification code)
#     """
# 
#     status = HTTPStatus.ACCEPTED
# 
#     default_message = _("Operation is in progress.")
# 
#     def __init__(self, message=None, data: dict = None):
#         super().__init__(message)
#         self.data = data or {}
# 
# class BadRequest(EsteidError):
#     pass
# 
# class EsteidError(Exception):
#     """
#     A generic Esteid error.
# 
#     Provides an interface for displaying a translated message to the user.
# 
#     Note: by default, any message passed as a first positional argument is ignored, because it only
#     serves for better logging. The User Interface should display a verbose error message
#     in case of a user-caused error so that user can take action accordingly. Errors caused by service malfunction
#     are likely not informative to a user without malicious intent.
# 
#     If the default message contains {key} placeholders, the exception is supposed to be
#     raised with kwargs, these will be passed on to <translated default message>.format(**kwargs).
#     """
# 
#     status = HTTPStatus.INTERNAL_SERVER_ERROR
# 
#     default_message = _("Something went wrong")
# 
#     kwargs: dict
# 
#     def __init__(self, message=None, **kwargs):
#         super().__init__(message)
#         self.kwargs = kwargs
# 
#     def get_message(self):
#         return str(self.default_message).format(**self.kwargs)
# 
#     def get_user_error(self):
#         return {
#             "error": self.__class__.__name__,  # error code that can be bound to JS translations
#             # TODO review errors and add translations
#             "message": self.get_message(),
#         }
# 
# class InvalidCredentials(EsteidError):
#     pass
# 
# class OfflineError(UpstreamServiceError):
#     default_message = _("The {service} service is unavailable at the moment. Please try again later.")
# 
# class SessionDoesNotExist(EsteidError):
#     pass
# 
# class UnsupportedClientImplementation(UpstreamServiceError):
#     pass
# 
# class UpstreamServiceError(EsteidError):
#     """A failure reported by upstream service which is not caused by user.
# 
#     Takes a `service` kwarg, e.g. Mobile ID / Smart ID
#     """
# 
#     status = HTTPStatus.SERVICE_UNAVAILABLE
# 
#     default_message = _("The {service} service reported a failure.")
# 
#     def __init__(self, message=None, *, service, **kwargs):
#         super().__init__(message)
#         self.kwargs = {"service": service, **kwargs}


# Path: esteid/base_service.py
import logging
import requests
from typing import Union
from uuid import UUID
from .exceptions import (
    ActionInProgress,
    BadRequest,
    EsteidError,
    InvalidCredentials,
    OfflineError,
    SessionDoesNotExist,
    UnsupportedClientImplementation,
    UpstreamServiceError,
)

        try:
            return response.json()
        except ValueError as e:  # pragma: no cover
            logger.exception("Failed to parse %s response as JSON:\n%s", req.url, response.content)
            raise UpstreamServiceError("Failed to parse response as JSON", service=self.NAME) from e

    def poll_session(self, session_id, endpoint_url, timeout=None) -> dict:
        """
        Polls session status with a specified timeout and returns the received result.

        The timeout defaults to 500ms to avoid extremely long blocking. If it is done asynchronously via Celery,
        it can be increased as per the docs below.

        https://github.com/SK-EID/MID#334-long-polling
        https://github.com/SK-EID/smart-id-documentation#46-session-status
        """
        endpoint_url = endpoint_url.format(session_id=session_id)
        try:
            data = self.invoke(
                endpoint_url,
                query={
                    "timeoutMs": timeout or 500,
                },
            )

        except UpstreamServiceError as e:
            cause = getattr(e, "__cause__", None)
            if isinstance(cause, requests.HTTPError):
                response = cause.response  # pylint: disable=no-member
                if response.status_code == 404:

                    raise SessionDoesNotExist(session_id) from e