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

from maps_adv.geosmb.landlord.proto.common_pb2 import Error
from maps_adv.geosmb.landlord.server.lib.exceptions import (
    BizIdHasLandingData,
    InvalidFetchToken,
    NoBizIdForOrg,
    NoDataForBizId,
    NoDataForSlug,
    NoOrginfo,
    NoOrgsForBizId,
    NoStableVersionForPublishing,
    SlugInUse,
    ToMuchSlugs,
    UnknownColorPreset,
    UnsupportedFieldForSuggest,
    VersionDoesNotExist,
)

__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 BizIdHasLandingData:
        code = Error.BIZ_ID_ALREADY_HAS_DATA
    except NoDataForBizId:
        code = Error.BIZ_ID_UNKNOWN
    except NoDataForSlug:
        return Response(status=404)
    except NoOrgsForBizId:
        code = Error.NO_ORGS_FOR_BIZ_ID
    except NoBizIdForOrg:
        code = Error.NO_BIZ_ID_FOR_ORG
    except NoOrginfo:
        code = Error.NO_ORGINFO
    except SlugInUse:
        code = Error.SLUG_IN_USE
    except ToMuchSlugs:
        code = Error.TOO_MUCH_SLUGS
    except VersionDoesNotExist:
        code = Error.VERSION_DOES_NOT_EXIST
    except InvalidFetchToken:
        return Response(status=401)
    except NoStableVersionForPublishing:
        code = Error.NO_STABLE_VERSION_FOR_PUBLISHING
    except UnsupportedFieldForSuggest:
        return Response(status=404)
    except UnknownColorPreset as exc:
        code = Error.UNKNOWN_COLOR_PRESET
        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))
