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

import gzip
import io
import json
import logging
import traceback
from collections import defaultdict
from email.message import EmailMessage
from typing import Dict, Tuple

from indico.errors import IndicoDecodingError

logger = logging.getLogger(__name__)


def decompress(response):
    response.raw.decode_content = True
    value = io.BytesIO(response.raw.data).getvalue()
    return gzip.decompress(value)


def deserialize(response, force_json=False, force_decompress=False):
    content_type, params = parse_header(response.headers.get("Content-Type"))

    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, force_json=False, force_decompress=False):
    content_type, params = parse_header(response.headers.get("Content-Type"))
    content = await response.read()

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

    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")
        )


def parse_header(header: str) -> Tuple[str, Dict[str, str]]:
    email = EmailMessage()
    email["Content-Type"] = header
    return email.get_content_type(), email["Content-Type"].params


def raw_bytes(content, *args, **kwargs):
    return content


def msgpack_deserialization(content, charset):
    try:
        import msgpack
    except ImportError as error:
        raise RuntimeError(
            "msgpack deserialization requires additional dependencies: "
            "`pip install indico-client[deserialization]`"
        ) from error

    return msgpack.unpackb(content)


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


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


def image_serialization(content, charset=None):
    return content


def zip_serialization(content, charset=None):
    return content


_SERIALIZATION_FNS = 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/msexcel": 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/vnd.ms-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,
    },
)
