from typing import (
    Dict,
    Optional,
    Union,
)

from ._DataType import DataType
from ._DefaultToken import DefaultToken
from ._Message import Message


class HttpRequestMessage(Message):
    def __init__(self, url, method, body=None, query=None, http_headers=None, context=None, timeout=30000):
        # type: (str, str, Optional[Union[str, bytes]], Optional[Union[str, bytes]], Optional[Dict[str, str]], Optional[str], int) -> None # noqa
        self.url = url
        self.method = method
        self.query = query if query is not None else {}
        self.body = body
        self.http_headers = http_headers if http_headers is not None else {}

        if context is None and "Host" in self.http_headers:
            context = self.http_headers["Host"]
        if context is None and "host" in self.http_headers:
            context = self.http_headers["host"]

        data = self.url.encode("utf-8") + b"\0"
        data += self.method.encode("utf-8") + b"\0"
        data += self._convert_dict_to_bytes(self.query)
        data += self._convert_dict_to_bytes(self.http_headers)

        if self.body is not None:
            if isinstance(self.body, type(u"")):
                data += self.body.encode("utf-8")
            else:
                data += self.body

        super(HttpRequestMessage, self).__init__(
            DefaultToken.TK_HTTP_MSG,
            DataType.http_request,
            data,
            timeout=timeout,
            headers={"context": context}
        )

    def __str__(self):
        # type: () -> str
        return str({
            "url": self.url,
            "method": self.method,
            "body": self.body,
            "headers": self.http_headers,
            "timeout": self.timeout,
            "message_headers": self.headers
        })

    @staticmethod
    def _convert_dict_to_bytes(d):
        ret = b""
        ret += str(len(d)).encode("utf-8") + b"\0"
        for k in d.keys():
            ret += k.encode("utf-8") + b"\0" + d[k].encode("utf-8") + b"\0"
        return ret
