import json
import logging

from aiohttp.web import Response, middleware
from marshmallow import ValidationError

from maps_adv.points.proto.errors_pb2 import Error
from maps_adv.points.server.lib.exceptions import (
    CollectionNotFound,
    InvalidPolygon,
    InvalidVersion,
    NonClosedPolygon,
    NoPointsPassed,
    NoPolygonsPassed,
)

__all__ = [
    "handle_collection_not_found_exception",
    "handle_invalid_version_exception",
    "handle_polygon_properties_exception",
    "handle_validation_error_exception",
    "handle_points_properties_exception",
]

logger = logging.getLogger(__name__)


@middleware
async def handle_collection_not_found_exception(request, handler):
    try:
        return await handler(request)
    except CollectionNotFound as exc:
        logger.exception(exc)
        error = Error(code=Error.COLLECTION_NOT_FOUND)
        return Response(status=400, body=error.SerializeToString())


@middleware
async def handle_polygon_properties_exception(request, handler):
    try:
        return await handler(request)
    except InvalidPolygon as exc:
        logger.exception(exc)
        error = Error(code=Error.INVALID_POLYGON)
        return Response(status=400, body=error.SerializeToString())
    except NonClosedPolygon as exc:
        logger.exception(exc)
        error = Error(code=Error.NON_CLOSED_POLYGON)
        return Response(status=400, body=error.SerializeToString())
    except NoPolygonsPassed as exc:
        logger.exception(exc)
        error = Error(code=Error.NO_POLYGONS_PASSED)
        return Response(status=400, body=error.SerializeToString())


@middleware
async def handle_points_properties_exception(request, handler):
    try:
        return await handler(request)
    except NoPointsPassed as exc:
        logger.exception(exc)
        error = Error(code=Error.NO_POINTS_PASSED)
        return Response(status=400, body=error.SerializeToString())


@middleware
async def handle_invalid_version_exception(request, handler):
    try:
        return await handler(request)
    except InvalidVersion as exc:
        logger.exception(exc)
        error = Error(code=Error.INVALID_VERSION)
        return Response(status=400, body=error.SerializeToString())


@middleware
async def handle_validation_error_exception(request, handler):
    try:
        return await handler(request)
    except ValidationError as exc:
        logger.error("Serialization error: %s", exc.normalized_messages())
        error = Error(code=Error.VALIDATION_ERROR, description=json.dumps(exc.messages))
        return Response(status=400, body=error.SerializeToString())
