"""Middleware."""
from aiohttp import hdrs

from .common import (
    JSONAPI, JSONAPI_CONTENT_TYPE, JSONAPI_CONTENT_TYPE_PARSED,
    logger
)
from .errors import (
    Error, ErrorList, HTTPUnsupportedMediaType, HTTPNotAcceptable
)
from .helpers import best_match, get_mime_type_params
from .utils import error_to_response


async def jsonapi_middleware(app, handler):
    """Middleware for handling JSON API errors."""
    async def middleware_handler(request):
        try:
            route_name = request.match_info.route.name
            namespace = request.app[JSONAPI]['routes_namespace']

            if route_name and route_name.startswith('%s.' % namespace):
                request_ct = request.headers.get(hdrs.CONTENT_TYPE)

                content_type_error = \
                    f"Content-Type '{JSONAPI_CONTENT_TYPE}' is required."

                if request_ct is None and request.has_body:
                    raise HTTPUnsupportedMediaType(detail=content_type_error)

                if (request_ct is not None and
                    request_ct != JSONAPI_CONTENT_TYPE):
                    raise HTTPUnsupportedMediaType(detail=content_type_error)

                accept_header = request.headers.get(hdrs.ACCEPT, '*/*')
                matched_mt, parsed_mt = best_match(
                    (JSONAPI_CONTENT_TYPE,), accept_header
                )
                if matched_mt != JSONAPI_CONTENT_TYPE:
                    raise HTTPNotAcceptable()

                if JSONAPI_CONTENT_TYPE_PARSED[:2] == parsed_mt[:2]:
                    additional_params = get_mime_type_params(parsed_mt)
                    if additional_params:
                        formatted = ','.join(
                            f'{k}={v}' for k, v in additional_params.items()
                        )
                        detail = (f'JSON API media type is modified '
                                  f'with media type parameters. ({formatted})')
                        raise HTTPNotAcceptable(detail=detail)

            return await handler(request)
        except Exception as exc:
            if isinstance(exc, (Error, ErrorList)):
                if app[JSONAPI]['log_errors']:
                    logger.exception(exc)
                return error_to_response(request, exc)
            else:
                raise

    return middleware_handler
