import base64
import logging
import os
from io import BytesIO
from typing import Any
from typing import Callable
from typing import Dict

from .http_headers import HttpHeaders
from .http_query_string import HttpQueryString
from .http_request import HttpRequest
from .http_response import HttpResponse
from .routing import Route
from urllib.parse import quote_plus

logger = logging.getLogger()
logger.setLevel(logging.INFO)


IS_SERVERLESS_ENVIRONMENT = bool(
    os.environ.get("AWS_LAMBDA_FUNCTION_VERSION") or
    os.environ.get("LAMBDA_RUNTIME_DIR") or
    os.environ.get("AWS_LAMBDA_FUNCTION_NAME")
)


def create_http_request_from_serverless(event: Dict[str, Any], context: Dict[str, Any]) -> HttpRequest:
    logger.info("converting serverless to http request")
    logger.info(event)
    logger.info(context)
    logger.info(os.environ)

    if event.get("version") and event["version"] == "2.0":
        return create_http_request_from_serverless_http_api(event, context)

    return create_http_request_from_serverless_rest_api(event, context)


def create_http_request_from_serverless_http_api(event: Dict[str, Any], context: Dict[str, Any]) -> HttpRequest:
    body = get_normalised_body_from_serverless(event)
    request_context = event.get("requestContext", {})
    http_context = request_context.get("http", {})

    headers = get_normalised_headers_from_serverless(event)
    headers["Cookie"] = "; ".join(event.get("cookies", []))
    headers["Content-Length"] = str(body.getbuffer().nbytes)

    request = HttpRequest(
        method=http_context["method"],
        path=http_context["path"],
        body=body,
        query_string=HttpQueryString(event.get("rawQueryString", "")),
        headers=HttpHeaders(headers)
    )
    request.path_parameters = event.get("pathParameters", {})

    request.attributes["aws_context"] = context
    request.attributes["aws_event"] = event

    return request


def create_http_request_from_serverless_rest_api(event: Dict[str, Any], context: Dict[str, Any]) -> HttpRequest:
    body = get_normalised_body_from_serverless(event)

    headers = get_normalised_headers_from_serverless(event)
    headers["Content-Length"] = str(body.getbuffer().nbytes)

    raw_query_string = ""
    for key, values in event.get("multiValueQueryStringParameters", {}):
        for value in values:
            raw_query_string += f"&{key}={quote_plus(value)}"

    request = HttpRequest(
        method=event.get("httpMethod", "GET"),
        path=event.get("path", "/"),
        body=body,
        query_string=HttpQueryString(raw_query_string[1:]),
        headers=HttpHeaders(headers)
    )
    request.path_parameters = event.get("pathParameters", {})

    request.attributes["aws_context"] = context
    request.attributes["aws_event"] = event

    return request


def get_normalised_headers_from_serverless(event: Dict[str, Any]) -> Dict[str, str]:
    headers = event["headers"]
    request_context = event.get("requestContext", {})  # Set serverless related additional headers
    if request_context.get("requestId"):
        headers["x-serverless-request-id"] = request_context.get("requestId")
    if request_context.get("stage"):
        headers["x-serverless-stage"] = request_context.get("stage")
    if headers.get("x-amzn-trace-id"):
        headers["x-serverless-trace-id"] = headers["x-amzn-trace-id"]

    return headers


def get_normalised_body_from_serverless(event: Dict[str, Any]) -> BytesIO:
    body = event.get("body", "")
    if event.get("isBase64Encoded", False):
        body = base64.b64decode(body)

    if isinstance(body, str):
        body = body.encode("utf-8")

    return BytesIO(body)


def make_serverless_callback(func: Callable[[HttpRequest], HttpResponse], route: Route) -> Callable:
    def _handle_serverless_request(event: Dict[str, Any], context: Dict[str, Any]) -> dict:
        request = create_http_request_from_serverless(event, context)
        route._parameters = request.path_parameters
        request.route = route
        response = func(request)

        normalised_headers = {}
        for key, value in response.headers.items():
            normalised_headers[key] = value

        response.body.seek(0)
        body = response.body.read()

        return {
            "isBase64Encoded": True,
            "statusCode": response.status_code,
            "headers": normalised_headers,
            "body": base64.b64encode(body),
        }

    return _handle_serverless_request
