{% include "api_info.j2" %}
from __future__ import annotations

import collections
from typing import Any, Dict, List, Union
import warnings

from {{ package }}.api_client import ApiClient, Endpoint as _Endpoint
from {{ package }}.model_utils import (
    date,
    datetime,
    set_attribute_from_path,
    get_attribute_from_path,
    file_type,
    none_type,
    UnsetType,
    unset,
)
{%- for model in get_api_models(operations) %}
from {{ package }}.{{ version }}.model.{{ model|safe_snake_case }} import {{ model }}
{%- endfor %}

{% set classname = name.replace(" ", "") + "Api" %}
class {{ classname }}:
    """
    {{ description|docstring|indent(4) }}
    """
    def __init__(self, api_client=None):
        if api_client is None:
            api_client = ApiClient()
        self.api_client = api_client
{% for path, method, operation in operations|sort(attribute="2.operationId") %}
    {%- set httpMethod = method.upper() %}
    {%- set returnType = operation|return_type %}
        self._{{ operation.operationId|safe_snake_case }}_endpoint = _Endpoint(
            settings={
                "response_type": {% if returnType %}({{ returnType }},){% else %}None{% endif %},
    {%- set authMethods = operation.security if "security" in operation else openapi.security %}
                "auth": [
{%- for auth in (authMethods or []) %}"{{ '", "'.join(auth.keys()) }}"{%- if not loop.last %}, {% endif %}{%- endfor %}],
                "endpoint_path": "{{ path }}",
                "operation_id": "{{ operation.operationId|safe_snake_case }}",
                "http_method": "{{httpMethod}}",
                "version": "{{ version }}",
{%- set servers = operation.servers %}
{%- if servers %}
                "servers": [
{%- for server in servers %}
                    {
                        "url": "{{ server.url }}",
                        "variables": {
{%- for name, variable in server.get("variables", {}).items() %}
                            "{{ name }}": {
                                "description": "{{ variable.description }}",
                                "default_value": "{{ variable.default }}",
{%- for value in variable.enum %}
{%- if loop.first %}
                                "enum_values": [
{%- endif %}
                                    "{{ value }}",
{%- if loop.last %}
                                ],
{%- endif %}
{%- endfor %}
                            },
{%- endfor %}
                        },
                    },
{%- endfor %}
                ],
{%- else %}
                "servers": None,
{%- endif %}
            },
            params_map={
      {%- for name, parameter in operation|parameters %}
      {%- set schema = parameter|parameter_schema %}
                "{{ name|attribute_name }}": {
                    {%- if parameter.required %}
                    "required": True,{% endif %}
                    {%- if parameter.nullable %}"nullable": True,{% endif %}
{%- set has_validation = (
      (not schema.nullable and required) or
      schema.maxLength is defined or
      schema.minLength is defined or
      schema.maximum is defined or
      schema.minimum is defined or
      schema.pattern is defined or
      schema.maxItems is defined or
      schema.minItems is defined
    ) %}
{%- if has_validation %}
                    "validation": {
{%- if schema.maxLength is defined %}
                        "max_length": {{ schema.maxLength }},
{%- endif %}
{%- if schema.minLength is defined %}
                        "min_length": {{ schema.minLength }},
{%- endif %}
{%- if schema.maxItems is defined %}
                        "max_items": {{ schema.maxItems }},
{%- endif %}
{%- if schema.minItems is defined %}
                        "min_items": {{ schema.minItems }},
{%- endif %}
{%- if schema.maximum is defined %}
                        "{% if schema.exclusiveMaximum %}exclusive{% else %}inclusive{% endif %}_maximum": {{ schema.maximum }},
{%- endif %}
{%- if schema.minimum is defined %}
                        "{% if schema.exclusiveMinimum %}exclusive{% else %}inclusive{% endif %}_minimum": {{ schema.minimum }},
{%- endif %}
                    },
{%- endif %}
                    "openapi_types": ({{ get_type_for_parameter(parameter) }},),
                    {%- if name == "body" %}
                    "location": "body",
                    {%- else %}
                    "attribute": "{{ name }}",
                    "location": "{{ parameter.in }}",
                    {%- endif %}
      {%- set collection = parameter|collection_format %}
      {%- if collection %}
                    "collection_format": "{{ collection }}",
      {%- endif %}
                },
       {%- endfor %}
            },
            headers_map={
                "accept": [
{%- for mediaType in operation|accept_headers %}"{{ mediaType }}"{%- if not loop.last %}, {% endif %}{%- endfor %}],
{%- if operation.requestBody %}
                "content_type": [
{%- for mediaType in operation.requestBody.content.keys() %}"{{ mediaType }}"{%- if not loop.last %}, {% endif %}{%- endfor %}]
{% else %}
                "content_type": [],
{%- endif %}
            },
            api_client=api_client,
        )
{% endfor %}

{%- for path, method, operation in operations|sort(attribute="2.operationId") %}
{%- set returnType = operation|return_type %}
    def {{ operation.operationId|safe_snake_case }}(self, {% for name, parameter in operation|parameters if parameter.required %}{{name|attribute_name}}: {{ get_type_for_parameter(parameter, typing=True) }}, {% endfor %}{% for name, parameter in operation|parameters if not parameter.required %}{% if loop.first %}*, {% endif %}{{name|attribute_name}}: Union[{{ get_type_for_parameter(parameter, typing=True) }}, UnsetType]=unset, {% endfor %}) -> {% if returnType %}{{ returnType.replace("[", "List[") }}{% else %}None{% endif %}:
        """{{ operation.summary|indent(8) }}.{% if operation.deprecated %} **Deprecated**.{% endif %}
{% if operation.description %}
        {{ operation.description|docstring|indent(8) }}
{% endif %}
{%- for name, parameter in operation|parameters if parameter.required %}
{%- if parameter.description %}
        :param {{ name|attribute_name }}: {{ parameter.description|docstring|indent(12) }}{% if parameter.default %} Defaults to {{ parameter.default }}.{% endif %}{% endif %}
        :type {{ name|attribute_name }}: {{ get_type_for_parameter(parameter) }}
{%- endfor %}
{%- for name, parameter in operation|parameters if not parameter.required %}
{%- if parameter.description %}
        :param {{ name|attribute_name }}: {{ parameter.description|docstring|indent(12) }}{%- if parameter.default %} If omitted the server will use the default value of {{ parameter.default }}.{% endif %}{% endif %}
        :type {{ name|attribute_name }}: {{ get_type_for_parameter(parameter) }}, optional
{%- endfor %}

{%- set returnType = operation|return_type %}
        :rtype: {% if returnType %}{{ returnType }}{% else %}None{% endif %}
        """
        kwargs: Dict[str, Any] = {}
{%- for name, parameter in operation|parameters %}
{%- if not parameter.required %}
        if {{ name|attribute_name }} is not unset:
            kwargs["{{ name|attribute_name }}"] = {{ name|attribute_name }}
{%- else %}
        kwargs["{{ name|attribute_name }}"] = {{ name|attribute_name }}
{%- endif %}
{% endfor %}
{%- if operation.deprecated %}
        warnings.warn("{{ operation.operationId|safe_snake_case }} is deprecated", DeprecationWarning, stacklevel=2)
{%- endif %}
        return self._{{ operation.operationId|safe_snake_case }}_endpoint.call_with_http_info(**kwargs)
    {%- if operation["x-pagination"] %}
    {%- set pagination = operation["x-pagination"] %}

    def {{ operation.operationId|safe_snake_case }}_with_pagination(self, {% for name, parameter in operation|parameters if parameter.required %}{{name|attribute_name}}: {{ get_type_for_parameter(parameter, typing=True) }}, {% endfor %}{% for name, parameter in operation|parameters if not parameter.required %}{% if loop.first %}*, {% endif %}{{name|attribute_name}}: Union[{{ get_type_for_parameter(parameter, typing=True) }}, UnsetType]=unset, {% endfor %}) -> collections.abc.Iterable[{{ get_type_at_path(operation, pagination.resultsPath) }}]:
        """{{ operation.summary|indent(8) }}.

        Provide a paginated version of :meth:`{{ operation.operationId|safe_snake_case }}`, returning all items.
{# keep new line #}
{%- for name, parameter in operation|parameters if parameter.required %}
{%- if parameter.description %}
        :param {{ name|attribute_name }}: {{ parameter.description|docstring|indent(12) }}{% if parameter.default %} Defaults to {{ parameter.default }}.{% endif %}{% endif %}
        :type {{ name|attribute_name }}: {{ get_type_for_parameter(parameter) }}
{%- endfor %}
{%- for name, parameter in operation|parameters if not parameter.required %}
{%- if parameter.description %}
        :param {{ name|attribute_name }}: {{ parameter.description|docstring|indent(12) }}{%- if parameter.default %} If omitted the server will use the default value of {{ parameter.default }}.{% endif %}{% endif %}
        :type {{ name|attribute_name }}: {{ get_type_for_parameter(parameter) }}, optional
{%- endfor %}

        :return: A generator of paginated results.
        :rtype: collections.abc.Iterable[{{ get_type_at_path(operation, pagination.resultsPath) }}]
        """
        kwargs: Dict[str, Any] = {}
{%- for name, parameter in operation|parameters %}
{%- if not parameter.required %}
        if {{ name|attribute_name }} is not unset:
            kwargs["{{ name|attribute_name }}"] = {{ name|attribute_name }}
{%- else %}
        kwargs["{{ name|attribute_name }}"] = {{ name|attribute_name }}
{%- endif %}
{% endfor %}
        local_page_size = get_attribute_from_path(kwargs, "{{ pagination.limitParam|attribute_path }}", {{ get_default(operation, pagination.limitParam) }})
        endpoint = self._{{ operation.operationId|safe_snake_case }}_endpoint
        set_attribute_from_path(kwargs, "{{ pagination.limitParam|attribute_path }}", local_page_size, endpoint.params_map)
        while True:
            response = endpoint.call_with_http_info(**kwargs)
            for item in get_attribute_from_path(response, "{{ pagination.resultsPath|attribute_path }}"):
                yield item
            if len(get_attribute_from_path(response, "{{ pagination.resultsPath|attribute_path }}")) < local_page_size:
                break
            {%- if pagination.pageOffsetParam %}
            set_attribute_from_path(kwargs, "{{ pagination.pageOffsetParam|attribute_path }}", get_attribute_from_path(kwargs, "{{ pagination.pageOffsetParam|attribute_path }}", 0) + local_page_size, endpoint.params_map)
            {%- endif %}
            {%- if pagination.cursorParam %}
            set_attribute_from_path(kwargs, "{{ pagination.cursorParam|attribute_path }}", get_attribute_from_path(response, "{{ pagination.cursorPath }}"), endpoint.params_map)
            {%- endif %}
    {%- endif %}
{% endfor %}
