from json.decoder import JSONDecodeError

from aiohttp import web
from marshmallow import ValidationError

from maps_adv.stat_controller.server.lib.data_managers import ConflictOperation
from maps_adv.stat_controller.server.lib.domains import (
    InProgressByAnotherExecutor,
    StatusSequenceViolation,
)

__all__ = [
    "handle_conflict_operation",
    "handle_json_decode_error",
    "handle_status_sequence_violation_error",
    "handle_unexpected_content_type",
    "handle_validation_error",
    "handlein_progress_by_another_executor_error",
]


@web.middleware
async def handle_json_decode_error(request, handler):
    try:
        return await handler(request)
    except JSONDecodeError as exc:
        return web.json_response(data={"json": exc.args}, status=400)


@web.middleware
async def handle_validation_error(request, handler):
    try:
        return await handler(request)
    except ValidationError as exc:
        return web.json_response(data=exc.messages, status=400)


@web.middleware
async def handlein_progress_by_another_executor_error(request, handler):
    try:
        return await handler(request)
    except InProgressByAnotherExecutor as exc:
        return web.json_response(data={"executor_id": exc.args}, status=400)


@web.middleware
async def handle_status_sequence_violation_error(request, handler):
    try:
        return await handler(request)
    except StatusSequenceViolation as exc:
        return web.json_response(data={"status": exc.args}, status=400)


@web.middleware
async def handle_unexpected_content_type(request, handler):
    expected_content_type = "application/json"
    content_type = request.content_type
    method = request.method

    if content_type != expected_content_type and method not in ["GET", "HEAD"]:
        return web.json_response(
            data={
                "Content-Type": [
                    f"Invalid content type {content_type}, "
                    f"expected {expected_content_type}."
                ]
            },
            status=415,
        )

    return await handler(request)


@web.middleware
async def handle_conflict_operation(req, handler):
    try:
        return await handler(req)
    except ConflictOperation:
        return web.json_response(
            data={"error": ["Request execution may result in a conflict."]}, status=409
        )
