from http import HTTPStatus
from typing import Any, Dict, List, Union, cast
from time import sleep
import random

import asyncio
import httpx

from ...client import AuthenticatedClient, Client
from ...types import Response, UNSET
from ... import errors

{% for relative in endpoint.relative_imports %}
{{ relative }}
{% endfor %}

{% from "endpoint_macros.py.jinja" import header_params, cookie_params, query_params, json_body, multipart_body,
    arguments, client, kwargs, parse_response, docstring %}

{% set return_string = endpoint.responses[0].prop.get_type_string() if endpoint.responses[0].prop.get_type_string() != "Any" else "None" %}
{% set parsed_responses = (endpoint.responses | length > 0) %}

def _get_kwargs(
    {{ arguments(endpoint) | indent(4) }}
) -> Dict[str, Any]:
    url = "{}{{ endpoint.path }}".format(
        client.base_url
        {%- for parameter in endpoint.path_parameters.values() -%}
        ,{{parameter.name}}={{parameter.python_name}}
        {%- endfor -%}
    )

    headers: Dict[str, str] = client.get_headers()
    cookies: Dict[str, Any] = client.get_cookies()

    {{ header_params(endpoint) | indent(4) }}

    {{ cookie_params(endpoint) | indent(4) }}

    {{ query_params(endpoint) | indent(4) }}

    {{ json_body(endpoint) | indent(4) }}

    {{ multipart_body(endpoint) | indent(4) }}

    return {
	    "method": "{{ endpoint.method }}",
        "url": url,
        "headers": headers,
        "cookies": cookies,
        "timeout": client.get_timeout(),
        "follow_redirects": client.follow_redirects,
        {% if endpoint.form_body %}
        "data": form_data.to_dict(),
        {% elif endpoint.multipart_body %}
        "files": {{ "multipart_" + endpoint.multipart_body.python_name }},
        {% elif endpoint.json_body %}
        "json": {{ "json_" + endpoint.json_body.python_name }},
        {% endif %}
        {% if endpoint.query_parameters %}
        "params": params,
        {% endif %}
    }


def _parse_response(*, client: Client, response: httpx.Response) -> {% if return_string == "None" %}Any{% else %}{{ return_string }}{% endif %}:
    {% for response in endpoint.responses %}
    if response.status_code == HTTPStatus.{{ response.status_code.name }}:
        {% import "property_templates/" + response.prop.template as prop_template %}
        {% if 200 <= response.status_code <= 299 %}
        {% if prop_template.construct %}
        {{ prop_template.construct(response.prop, response.source) | indent(8) }}
        {% else %}
        {{ response.prop.python_name }} = cast({{ response.prop.get_type_string() }}, {{ response.source }})
        {% endif %}
        return {{ response.prop.python_name }}
        {% else %}
        raise {{ response.prop.get_type_string() }}.init_exception({{ response.source }}, response.status_code)
        {% endif %}
    {% endfor %}
    raise errors.UnexpectedStatus(response.status_code, response.content)


def _build_response(*, client: Client, response: httpx.Response) -> Response[{{ return_string }}]:
    return Response(
        status_code=HTTPStatus(response.status_code),
        content=response.content,
        headers=response.headers,
        parsed=_parse_response(client=client, response=response),
    )


def request_sync_detailed(
    {{ arguments(endpoint) | indent(4) }}
) -> Response[{{ return_string }}]:
    {{ docstring(endpoint, return_string, is_detailed=true) | indent(4) }}

    kwargs = _get_kwargs(
        {{ kwargs(endpoint) }}
    )

    kwargs['headers'] = {'svix-req-id':f"{random.getrandbits(32)}", **kwargs['headers']}

    response = httpx.request(
        verify=client.verify_ssl,
        **kwargs,
    )
    for retry_count, sleep_time in enumerate(client.retry_schedule):
        if response.status_code < 500:
            break

        sleep(sleep_time)
        kwargs['headers']['svix-retry-count'] = str(retry_count)
        response = httpx.request(
            verify=client.verify_ssl,
            **kwargs,
        )

    return _build_response(client=client, response=response)

{% if parsed_responses %}
def request_sync(
    {{ arguments(endpoint) | indent(4) }}
) -> {{ return_string }}:
    {{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }}

    return request_sync_detailed(
        {{ kwargs(endpoint) }}
    ).parsed
{% endif %}

async def request_asyncio_detailed(
    {{ arguments(endpoint) | indent(4) }}
) -> Response[{{ return_string }}]:
    {{ docstring(endpoint, return_string, is_detailed=true) | indent(4) }}

    kwargs = _get_kwargs(
        {{ kwargs(endpoint) }}
    )

    kwargs['headers'] = {'svix-req-id':f"{random.getrandbits(32)}", **kwargs['headers']}

    async with httpx.AsyncClient(verify=client.verify_ssl) as _client:
        response = await _client.request(
            **kwargs
        )

        for retry_count, sleep_time in enumerate(client.retry_schedule):
            if response.status_code < 500:
                break

            await asyncio.sleep(sleep_time)
            kwargs['headers']['svix-retry-count'] = str(retry_count)
            response = await _client.request(
                **kwargs
            )

    return _build_response(client=client, response=response)

{% if parsed_responses %}
async def request_asyncio(
    {{ arguments(endpoint) | indent(4) }}
) -> {{ return_string }}:
    {{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }}

    return (await request_asyncio_detailed(
        {{ kwargs(endpoint) }}
    )).parsed
{% endif %}
