from datetime import datetime, date
from typing import List, Optional, Union, Set
from enum import Enum
from pydantic import BaseModel

{% set ns = namespace(abstract_found=false, id_found = false, same_name_found = false )-%}
{% set processed_associations = [] %}
{% for class in domain.get_classes() -%}
    {% if class.is_abstract -%}
        {% set ns.abstract_found = true -%}
    {% endif -%}    
{% endfor -%}
{% if ns.abstract_found %}
from abc import ABC, abstractmethod
{% endif %}

############################################
# Enumerations are defined here
############################################

{# Handle enums first #}
{% for enum in domain.get_enumerations() -%}
class {{ enum.name }}(Enum):
{% for literal in enum.literals %}
    {{ literal.name }} = "{{ literal.name }}"
{% endfor %}

{% endfor -%}

############################################
# Classes are defined here
############################################
{% for class in domain.get_classes() %}
class {{ class.name }}{% if backend %}Create{% endif %}{% if class.parents() %}({% for parent in class.parents() %}{{parent.name}}{% if backend %}Create{% endif %}{{ ", " if not loop.last else "" }}{% endfor %}){% elif class.is_abstract %}(ABC, BaseModel){% else %}(BaseModel){% endif %}:
    {% set ns.id_found = false %}
    {% for attribute in class.attributes %}
    {{ attribute.name }}: {{ attribute.type.name }}{% if attribute.is_id %}  # the id{% endif %}

    {% endfor %}
    {% if not ns.id_found and not ns.same_name_found and not backend %}
    id: int  # id created
    {% endif %}
    {%- for association in class.associations %}
        {%- if association.ends|length == 2 and association.name not in processed_associations %}
            {%- set ns.end1 = none %}
            {%- set ns.end2 = none %}
            {%- for end in association.ends %}
                {%- if end.type.name == class.name %}
                    {%- set ns.end1 = end %}
                {%- else %}
                    {%- set ns.end2 = end %}
                {%- endif %}
            {%- endfor %}
            {%- if ns.end1 and ns.end2 %}
                {%- if ns.end1.multiplicity.max > 1 and ns.end2.multiplicity.max > 1 %}
                    {%- if nested_creations %}
                        {%- if backend %}
    {{ ns.end2.name }}: Optional[List[Union["{{ ns.end2.type.name }}Create", int]]] = None  # N:M Relationship
                        {% endif %}
                        {%- if not backend %}
    {{ ns.end2.name }}: Set["{{ ns.end2.type.name }}"]  # N:M Relationship
                        {%- endif %}
                    {%- else %}
    {{ ns.end2.name }}: List[int]
                    {% endif %}
                {%- endif %}
                {% if ns.end1.multiplicity.max > 1 and ns.end2.multiplicity.max == 1 %}
    {{ ns.end2.name }}: int  # N:1 Relationship
                {% endif %}
                {%- if ns.end1.multiplicity.max == 1 and ns.end2.multiplicity.max == 1 and not backend %}
    {{ ns.end2.name }}: "{{ ns.end2.type.name }}"  # 1:1 Relationship
                {% endif %}
            {%- endif %}
            {%- if not backend %}
                {%- do processed_associations.append(association.name) %}
            {%- endif %}
        {%- endif %}
    {%- endfor %}
    {%- if not class.attributes and not class.associations %}
    pass
    {% endif %}

{% endfor %}