"""
Handles deserialization / decoding of responses
"""

import cgi
import gzip
import io
import json
import logging
import traceback
from collections import defaultdict
from typing import TYPE_CHECKING

import msgpack

from indico.errors import IndicoDecodingError

if TYPE_CHECKING:  # pragma: no cover
    from typing import Any, Callable, Mapping, Optional

    from aiohttp import ClientResponse
    from requests import Response

logger = logging.getLogger(__name__)


def decompress(response: "Response") -> bytes:
    response.raw.decode_content = True
    value: bytes = io.BytesIO(response.raw.data).getvalue()
    return gzip.decompress(value)


def deserialize(
    response: "Response", force_json: bool = False, force_decompress: bool = False
) -> "Any":
    content_type, params = cgi.parse_header(response.headers["Content-Type"])
    content: bytes

    if force_decompress or content_type in ["application/x-gzip", "application/gzip"]:
        content = decompress(response)
    else:
        content = response.content

    charset = params.get("charset", "utf-8")

    # For storage object for example where the content is json based on url ending
    if force_json:
        content_type = "application/json"

    try:
        return _SERIALIZATION_FNS[content_type](content, charset)
    except Exception:
        logger.debug(traceback.format_exc())
        raise IndicoDecodingError(
            content_type, charset, content.decode("ascii", "ignore")
        )


async def aio_deserialize(
    response: "ClientResponse", force_json: bool = False, force_decompress: bool = False
) -> "Any":
    content_type, params = cgi.parse_header(response.headers["Content-Type"])
    content: bytes = await response.read()

    if force_decompress or content_type in ["application/x-gzip", "application/gzip"]:
        content = gzip.decompress(io.BytesIO(content).getvalue())

    charset: str = params.get("charset", "utf-8")

    # For storage object for example where the content is json based on url ending
    if force_json:
        content_type = "application/json"

    try:
        return _SERIALIZATION_FNS[content_type](content, charset)
    except Exception:
        logger.debug(traceback.format_exc())
        raise IndicoDecodingError(
            content_type, charset, content.decode("ascii", "ignore")
        )


def raw_bytes(
    content: bytes, charset: "Optional[str]", *args: "Any", **kwargs: "Any"
) -> bytes:
    return content


def msgpack_deserialization(content: bytes, charset: "Optional[str]" = None) -> "Any":
    return msgpack.unpackb(content)


def json_deserialization(content: bytes, charset: str = "utf-8") -> "Any":
    return json.loads(content.decode(charset))


def text_deserialization(content: bytes, charset: str = "utf-8") -> str:
    return content.decode(charset)


def image_serialization(content: bytes, charset: "Optional[str]" = None) -> bytes:
    return content


def zip_serialization(content: bytes, charset: "Optional[str]" = None) -> bytes:
    return content


_SERIALIZATION_FNS: "Mapping[str, Callable[[bytes, str], Any]]" = defaultdict(
    lambda: text_deserialization,
    {
        "application/pdf": raw_bytes,
        "application/octet-stream": raw_bytes,
        "application/doc": raw_bytes,
        "application/ms-doc": raw_bytes,
        "application/msword": raw_bytes,
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document": raw_bytes,
        "application/vnd.oasis.opendocument.text": raw_bytes,
        "application/zip": zip_serialization,
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": raw_bytes,
        "application/vnd.ms-excel": raw_bytes,
        "application/msexcel": raw_bytes,
        "application/excel": raw_bytes,
        "application/x-dos_ms_excel": raw_bytes,
        "application/x-excel": raw_bytes,
        "application/x-ms-excel": raw_bytes,
        "application/x-xls": raw_bytes,
        "application/xlc": raw_bytes,
        "application/xls": raw_bytes,
        "application/xlt": raw_bytes,
        "application/vnd.ms-powerpoint": raw_bytes,
        "application/vnd.openxmlformats-officedocument.presentationml.presentation": raw_bytes,
        "application/mspowerpoint": raw_bytes,
        "application/powerpoint": raw_bytes,
        "application/x-mspowerpoint": raw_bytes,
        "image/png": image_serialization,
        "image/jpeg": image_serialization,
        "image/jpg": image_serialization,
        "image/tiff": image_serialization,
        "text/html": text_deserialization,
        "text/plain": text_deserialization,
        "text/rtf": text_deserialization,
        "message/rfc822": raw_bytes,
        "application/vnd.ms-outlook": raw_bytes,
        "application/vnd.ms-office": raw_bytes,
        "binary/octet-stream": raw_bytes,
        "application/rtf": text_deserialization,
        "text/csv": text_deserialization,
        "application/x-msgpack": msgpack_deserialization,
        "application/msgpack": msgpack_deserialization,
        "x-msgpack": msgpack_deserialization,
        "msgpack": msgpack_deserialization,
        "application/json": json_deserialization,
        "json": json_deserialization,
        "application/javascript": json_deserialization,
        "application/x-gzip": raw_bytes,
        "application/gzip": raw_bytes,
    },
)
