#  Copyright (C) 2021
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Mike Orlov <m.orlov@abm-jsc.ru>
#
from dataclasses import dataclass, field
from typing import Any, Optional
from urllib.parse import parse_qsl

import aiohttp.web_request
from multidict import CIMultiDictProxy

from http_tools.charset import Charset
from http_tools.mime_types import ContentType
from http_tools.multipart_form_data import parse_multipart_form_data, BodyPart
from http_tools.request_metadata import RequestMetadata


@dataclass
class IncomingRequest:
    url_query_key_to_value: dict[str, str]
    path_match_key_to_value: dict[str, str]
    parsed_body: dict[str, Any] | list[Any] | str | float | int | bool | None | CIMultiDictProxy[BodyPart]
    key_value_params: dict[str, Any]
    metadata: RequestMetadata
    request: aiohttp.web_request.Request
    payload: Optional[bytes] = field(repr=False, default=None)

    @property
    def key_value_arguments(self) -> dict[str, Any]:
        return self.key_value_params


async def request_from_web_request(web_request: aiohttp.web_request.Request) -> IncomingRequest:
    header_name_to_value = dict(web_request.headers)
    url_info: dict = web_request.match_info.get_info()
    route_path = url_info.get("formatter", url_info.get("path"))

    client_host = None
    peer_name = web_request.transport.get_extra_info("peername")[:2]
    if peer_name is not None:
        client_host, client_port = peer_name

    request_metadata = RequestMetadata(
        http_request_host=client_host, request_method=web_request.method, path_info=route_path,
        header_name_to_value=header_name_to_value, url=web_request.url
    )

    url_query = dict(**web_request.rel_url.query)
    path_match = dict(**web_request.match_info)
    parsed_body = None

    if web_request.content_type == ContentType.Json:
        parsed_body = await web_request.json()
    elif web_request.content_type == ContentType.MultipartForm:
        multipart_reader = await web_request.multipart()
        parsed_body = await parse_multipart_form_data(multipart_reader)
    elif web_request.content_type == ContentType.FormUrlEncoded:
        data = await web_request.read()
        if data:
            parsed_body = dict(parse_qsl(
                data.rstrip().decode(Charset.UTF8), keep_blank_values=True, encoding=Charset.UTF8
            ))
    key_value_params = url_query | path_match
    if isinstance(parsed_body, (dict, CIMultiDictProxy)):
        key_value_params.update(parsed_body)

    payload = await web_request.read()
    return IncomingRequest(
        url_query_key_to_value=url_query, path_match_key_to_value=path_match, parsed_body=parsed_body,
        key_value_params=key_value_params, metadata=request_metadata, payload=payload, request=web_request
    )
