from __future__ import annotations
from inspect import getfullargspec
import pprint
import re  # noqa: F401
import json
{{#hasChildren}}
{{#discriminator}}
import {{{modelPackage}}}
{{/discriminator}}
{{/hasChildren}}

{{#vendorExtensions.x-py-datetime-imports}}{{#-first}}from datetime import{{/-first}} {{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-py-datetime-imports}}
{{#vendorExtensions.x-py-typing-imports}}{{#-first}}from typing import{{/-first}} {{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-py-typing-imports}}
{{#vendorExtensions.x-py-pydantic-imports}}{{#-first}}from pydantic import{{/-first}} {{{.}}}{{^-last}},{{/-last}}{{/vendorExtensions.x-py-pydantic-imports}}
{{#vendorExtensions.x-py-model-imports}}
{{{.}}}
{{/vendorExtensions.x-py-model-imports}}

class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}):
    """NOTE: This class is auto generated by OpenAPI Generator.
    Ref: https://openapi-generator.tech

    Do not edit the class manually.
    """
{{#vars}}
    {{name}}: {{{vendorExtensions.x-py-typing}}}
{{/vars}}
{{#isAdditionalPropertiesTrue}}
    additional_properties: Dict[str, Any] = {}
{{/isAdditionalPropertiesTrue}}
    __properties = [{{#allVars}}"{{baseName}}"{{^-last}}, {{/-last}}{{/allVars}}]
{{#vars}}
    {{#vendorExtensions.x-regex}}

    @validator('{{{name}}}')
    def {{{name}}}_validate_regular_expression(cls, v):
        if not re.match(r"{{{.}}}", v{{#vendorExtensions.x-modifiers}} ,re.{{{.}}}{{/vendorExtensions.x-modifiers}}):
            raise ValueError(r"must validate the regular expression {{{pattern}}}")
        return v
    {{/vendorExtensions.x-regex}}
    {{#isEnum}}

    @validator('{{{name}}}')
    def {{{name}}}_validate_enum(cls, v):
        {{^required}}
        if v is None:
            return v

        {{/required}}
        if v not in ({{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}):
            raise ValueError("must validate the enum values ({{#allowableValues}}{{#enumVars}}{{{value}}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}})")
        return v
    {{/isEnum}}
{{/vars}}

    class Config:
        allow_population_by_field_name = True
        validate_assignment = True

{{#hasChildren}}
{{#discriminator}}
    # JSON field name that stores the object type
    __discriminator_property_name = '{{discriminator.propertyBaseName}}'

    {{#mappedModels}}
    {{#-first}}
    # discriminator mappings
    __discriminator_value_class_map = {
    {{/-first}}
        '{{{mappingName}}}': '{{{modelName}}}'{{^-last}},{{/-last}}
    {{#-last}}
    }

    @classmethod
    def get_discriminator_value(cls, obj: dict) -> str:
        """Returns the discriminator value (object type) of the data"""
        discriminator_value = obj[cls.__discriminator_property_name]
        if discriminator_value:
            return cls.__discriminator_value_class_map.get(discriminator_value)
        else:
            return None
    {{/-last}}
    {{/mappedModels}}

{{/discriminator}}
{{/hasChildren}}
    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.dict(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> {{^hasChildren}}{{{classname}}}{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union({{#children}}{{{classname}}}{{^-last}}, {{/-last}}{{/children}}){{/discriminator}}{{^discriminator}}{{{classname}}}{{/discriminator}}{{/hasChildren}}:
        """Create an instance of {{{classname}}} from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self):
        """Returns the dictionary representation of the model using alias"""
        _dict = self.dict(by_alias=True,
                          exclude={
                            {{#vendorExtensions.x-py-readonly}}
                            "{{{.}}}",
                            {{/vendorExtensions.x-py-readonly}}
                            {{#isAdditionalPropertiesTrue}}
                            "additional_properties"
                            {{/isAdditionalPropertiesTrue}}
                          },
                          exclude_none=True)
        {{#allVars}}
        {{#isContainer}}
        {{#isArray}}
        {{^items.isPrimitiveType}}
        {{^items.isEnumOrRef}}
        # override the default output from pydantic by calling `to_dict()` of each item in {{{name}}} (list)
        _items = []
        if self.{{{name}}}:
            for _item in self.{{{name}}}:
                if _item:
                    _items.append(_item.to_dict())
            _dict['{{{baseName}}}'] = _items
        {{/items.isEnumOrRef}}
        {{/items.isPrimitiveType}}
        {{/isArray}}
        {{#isMap}}
        {{^items.isPrimitiveType}}
        {{^items.isEnumOrRef}}
        # override the default output from pydantic by calling `to_dict()` of each value in {{{name}}} (dict)
        _field_dict = {}
        if self.{{{name}}}:
            for _key in self.{{{name}}}:
                if self.{{{name}}}[_key]:
                    _field_dict[_key] = self.{{{name}}}[_key].to_dict()
            _dict['{{{baseName}}}'] = _field_dict
        {{/items.isEnumOrRef}}
        {{/items.isPrimitiveType}}
        {{/isMap}}
        {{/isContainer}}
        {{^isContainer}}
        {{^isPrimitiveType}}
        {{^isEnumOrRef}}
        # override the default output from pydantic by calling `to_dict()` of {{{name}}}
        if self.{{{name}}}:
            _dict['{{{baseName}}}'] = self.{{{name}}}.to_dict()
        {{/isEnumOrRef}}
        {{/isPrimitiveType}}
        {{/isContainer}}
        {{/allVars}}
        {{#isAdditionalPropertiesTrue}}
        # puts key-value pairs in additional_properties in the top level
        if self.additional_properties is not None:
            for _key, _value in self.additional_properties.items():
                _dict[_key] = _value

        {{/isAdditionalPropertiesTrue}}
        {{#allVars}}
        {{#isNullable}}
        # set to None if {{{name}}} (nullable) is None
        if self.{{name}} is None:
            _dict['{{{baseName}}}'] = None

        {{/isNullable}}
        {{/allVars}}
        return _dict

    @classmethod
    def from_dict(cls, obj: dict) -> {{^hasChildren}}{{{classname}}}{{/hasChildren}}{{#hasChildren}}{{#discriminator}}Union({{#children}}{{{classname}}}{{^-last}}, {{/-last}}{{/children}}){{/discriminator}}{{^discriminator}}{{{classname}}}{{/discriminator}}{{/hasChildren}}:
        """Create an instance of {{{classname}}} from a dict"""
        {{#hasChildren}}
        {{#discriminator}}
        # look up the object type based on discriminator mapping
        object_type = cls.get_discriminator_value(obj)
        if object_type:
            klass = getattr({{modelPackage}}, object_type)
            return klass.from_dict(obj)
        else:
            raise ValueError("{{{classname}}} failed to lookup discriminator value from " +
                             json.dumps(obj) + ". Discriminator property name: " + cls.__discriminator_property_name +
                             ", mapping: " + json.dumps(cls.__discriminator_value_class_map))
        {{/discriminator}}
        {{/hasChildren}}
        {{^hasChildren}}
        if obj is None:
            return None

        if type(obj) is not dict:
            return {{{classname}}}.parse_obj(obj)

        {{#disallowAdditionalPropertiesIfNotPresent}}
        {{^isAdditionalPropertiesTrue}}
        # raise errors for additional fields in the input
        for _key in obj.keys():
            if _key not in cls.__properties:
                raise ValueError("Error due to additional fields (not defined in {{classname}}) in the input: " + obj)

        {{/isAdditionalPropertiesTrue}}
        {{/disallowAdditionalPropertiesIfNotPresent}}
        _obj = {{{classname}}}.parse_obj({
            {{#allVars}}
            {{#isContainer}}
            {{#isArray}}
            {{^items.isPrimitiveType}}
            {{#items.isEnumOrRef}}
            "{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
            {{/items.isEnumOrRef}}
            {{^items.isEnumOrRef}}
            "{{{name}}}": [{{{items.dataType}}}.from_dict(_item) for _item in obj.get("{{{baseName}}}")] if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
            {{/items.isEnumOrRef}}
            {{/items.isPrimitiveType}}
            {{#items.isPrimitiveType}}
            "{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
            {{/items.isPrimitiveType}}
            {{/isArray}}
            {{#isMap}}
            {{^items.isPrimitiveType}}
            {{^items.isEnumOrRef}}
            "{{{name}}}": dict((_k, {{{dataType}}}.from_dict(_v)) for _k, _v in obj.get("{{{baseName}}}").items()){{^-last}},{{/-last}}
            {{/items.isEnumOrRef}}
            {{#items.isEnumOrRef}}
            "{{{name}}}": dict((_k, _v) for _k, _v in obj.get("{{{baseName}}}").items()){{^-last}},{{/-last}}
            {{/items.isEnumOrRef}}
            {{/items.isPrimitiveType}}
            {{#items.isPrimitiveType}}
            "{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
            {{/items.isPrimitiveType}}
            {{/isMap}}
            {{/isContainer}}
            {{^isContainer}}
            {{^isPrimitiveType}}
            {{^isEnumOrRef}}
            "{{{name}}}": {{{dataType}}}.from_dict(obj.get("{{{baseName}}}")) if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
            {{/isEnumOrRef}}
            {{#isEnumOrRef}}
            "{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
            {{/isEnumOrRef}}
            {{/isPrimitiveType}}
            {{#isPrimitiveType}}
            {{#defaultValue}}
            "{{{name}}}": obj.get("{{{baseName}}}") if obj.get("{{{baseName}}}") is not None else {{{defaultValue}}}{{^-last}},{{/-last}}
            {{/defaultValue}}
            {{^defaultValue}}
            "{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
            {{/defaultValue}}
            {{/isPrimitiveType}}
            {{/isContainer}}
            {{/allVars}}
        })
        {{#isAdditionalPropertiesTrue}}
        # store additional fields in additional_properties
        for _key in obj.keys():
            if _key not in cls.__properties:
                _obj.additional_properties[_key] = obj.get(_key)

        {{/isAdditionalPropertiesTrue}}
        return _obj
        {{/hasChildren}}