# Path: lepo/api_info.py
# class APIInfo:
#     def __init__(self, operation, router=None):
#         """
#         :type operation: lepo.operation.Operation
#         :type router: lepo.router.Router
#         """
#         self.api = operation.api
#         self.path = operation.path
#         self.operation = operation
#         self.router = router
# 
# Path: lepo/excs.py
# class ExceptionalResponse(Exception):
#     """
#     Wraps a Response in an exception.
# 
#     These exceptions are caught in PathView.
#     """
#     def __init__(self, response):
#         self.response = response
# 
# class InvalidOperation(ValueError):
#     pass
# 
# Path: lepo/parameter_utils.py
# def read_parameters(request, view_kwargs=None, capture_errors=False):  # noqa: C901
#     """
#     :param request: HttpRequest with attached api_info
#     :type request: HttpRequest
#     :type view_kwargs: dict[str, object]
#     :type capture_errors: bool
#     :rtype: dict[str, object]
#     """
#     if view_kwargs is None:
#         view_kwargs = {}
#     params = {}
#     errors = {}
#     for param in request.api_info.operation.parameters:
#         try:
#             value = param.get_value(request, view_kwargs)
#             if value is NO_VALUE:
#                 if param.has_default:
#                     params[param.name] = param.default
#                 elif param.required:  # Required but missing
#                     errors[param.name] = MissingParameter(f'parameter {param.name} is required but missing')
#                 continue  # No value, or a default was added, or an error was added.
#             params[param.name] = param.cast(request.api_info.api, value)
#         except (NotImplementedError, ImproperlyConfigured):
#             raise
#         except Exception as e:
#             if not capture_errors:
#                 raise
#             errors[param.name] = e
#     if errors:
#         raise ErroneousParameters(errors, params)
#     return params
# 
# Path: lepo/utils.py
# def snake_case(string):
#     return camel_case_to_spaces(string).replace('-', '_').replace(' ', '_').replace('__', '_')


from django.http import HttpResponse, JsonResponse
from django.views import View
from lepo.api_info import APIInfo
from lepo.excs import ExceptionalResponse, InvalidOperation
from lepo.parameter_utils import read_parameters
from lepo.utils import snake_case

class PathView(View):
    router = None  # Filled in by subclasses
    path = None  # Filled in by subclasses

    def dispatch(self, request, **kwargs):
        try:
            operation = self.path.get_operation(request.method)
        except InvalidOperation:
            return self.http_method_not_allowed(request, **kwargs)
        request.api_info = APIInfo(
            operation=operation,
            router=self.router,
        )
        params = {
            snake_case(name): value
            for (name, value)
            in read_parameters(request, kwargs, capture_errors=True).items()
        }
        handler = request.api_info.router.get_handler(operation.id)
        try:
            response = handler(request, **params)
        except ExceptionalResponse as er:
            response = er.response

        return self.transform_response(response)

    def transform_response(self, response):
        if isinstance(response, HttpResponse):
            # TODO: validate against responses

# Path: lepo/api_info.py
# class APIInfo:
#     def __init__(self, operation, router=None):
#         """
#         :type operation: lepo.operation.Operation
#         :type router: lepo.router.Router
#         """
#         self.api = operation.api
#         self.path = operation.path
#         self.operation = operation
#         self.router = router
# 
# Path: lepo/excs.py
# class ExceptionalResponse(Exception):
#     """
#     Wraps a Response in an exception.
# 
#     These exceptions are caught in PathView.
#     """
#     def __init__(self, response):
#         self.response = response
# 
# class InvalidOperation(ValueError):
#     pass
# 
# Path: lepo/parameter_utils.py
# def read_parameters(request, view_kwargs=None, capture_errors=False):  # noqa: C901
#     """
#     :param request: HttpRequest with attached api_info
#     :type request: HttpRequest
#     :type view_kwargs: dict[str, object]
#     :type capture_errors: bool
#     :rtype: dict[str, object]
#     """
#     if view_kwargs is None:
#         view_kwargs = {}
#     params = {}
#     errors = {}
#     for param in request.api_info.operation.parameters:
#         try:
#             value = param.get_value(request, view_kwargs)
#             if value is NO_VALUE:
#                 if param.has_default:
#                     params[param.name] = param.default
#                 elif param.required:  # Required but missing
#                     errors[param.name] = MissingParameter(f'parameter {param.name} is required but missing')
#                 continue  # No value, or a default was added, or an error was added.
#             params[param.name] = param.cast(request.api_info.api, value)
#         except (NotImplementedError, ImproperlyConfigured):
#             raise
#         except Exception as e:
#             if not capture_errors:
#                 raise
#             errors[param.name] = e
#     if errors:
#         raise ErroneousParameters(errors, params)
#     return params
# 
# Path: lepo/utils.py
# def snake_case(string):
#     return camel_case_to_spaces(string).replace('-', '_').replace(' ', '_').replace('__', '_')


# Path: lepo/path_view.py
from django.http import HttpResponse, JsonResponse
from django.views import View
from lepo.api_info import APIInfo
from lepo.excs import ExceptionalResponse, InvalidOperation
from lepo.parameter_utils import read_parameters
from lepo.utils import snake_case


class PathView(View):
    router = None  # Filled in by subclasses
    path = None  # Filled in by subclasses

    def dispatch(self, request, **kwargs):
        try:
            operation = self.path.get_operation(request.method)
        except InvalidOperation:
            return self.http_method_not_allowed(request, **kwargs)
        request.api_info = APIInfo(
            operation=operation,
            router=self.router,
        )
        params = {
            snake_case(name): value
            for (name, value)
            in read_parameters(request, kwargs, capture_errors=True).items()
        }
        handler = request.api_info.router.get_handler(operation.id)
        try:
            response = handler(request, **params)
        except ExceptionalResponse as er:
            response = er.response

        return self.transform_response(response)

    def transform_response(self, response):
        if isinstance(response, HttpResponse):
            # TODO: validate against responses

            return response