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

from maps_adv.geosmb.harmonist.proto.pipeline_pb2 import Error
from maps_adv.geosmb.harmonist.server.lib.exceptions import (
    BadFileContent,
    ClientsAlreadyWereImported,
    InvalidSessionId,
    MarkUpAlreadySubmitted,
    ValidationNotFinished,
)

__all__ = ["handle_validation_errors", "handle_domain_errors"]


@middleware
async def handle_validation_errors(req: Request, handler) -> Response:
    try:
        return await handler(req)
    except ValidationError as exc:
        if isinstance(exc.messages, dict):
            description = _dict_to_str(exc.messages)
        else:
            description = ", ".join(sorted(exc.messages))

        error = Error(code=Error.VALIDATION_ERROR, description=description)
        return Response(status=400, body=error.SerializeToString())


@middleware
async def handle_domain_errors(req: Request, handler) -> Response:
    status, code, description = 400, None, None
    try:
        return await handler(req)
    except InvalidSessionId as exc:
        code = Error.INVALID_SESSION_ID
        description = exc.args[0]
    except MarkUpAlreadySubmitted as exc:
        code = Error.MARKUP_ALREADY_SUBMITTED
        description = exc.args[0]
    except BadFileContent as exc:
        code = Error.BAD_FILE_CONTENT
        description = exc.args[0]
    except ClientsAlreadyWereImported as exc:
        code = Error.CLIENTS_ALREADY_WERE_IMPORTED
        description = exc.args[0]
    except ValidationNotFinished as exc:
        code = Error.VALIDATION_NOT_FINISHED
        description = exc.args[0]

    error = Error(code=code)
    if description is not None:
        error.description = description

    return Response(status=status, body=error.SerializeToString())


def _dict_to_str(data: dict, separator=": ") -> str:
    return ", ".join(f"{field}{separator}{data[field]}" for field in sorted(data))
