from __future__ import annotations
from typing import Union
from datafinder.typed_attributes import *
from datafinder import FinderResult, convert_inputs_and_select
from datafinder.finder import RelatedFinder
from model.relational import Column, Table, NoOperation, JoinOperation, JoinTreeNodeOperation, CountAllOperation
from model.milestoning import ProcessingTemporalColumns, SingleBusinessDateColumn, BusinessDateAndProcessingTemporalColumns, BiTemporalColumns, MilestonedTable
from {{class_module_map_base[rcm.clazz.name]}} import {{rcm.clazz.name}}FinderBase, {{rcm.clazz.name}}RelatedFinderBase

{% for rpm in rcm.property_mappings %}
{% if not is_primitive(rpm.property) and rpm.property.type.name != rcm.clazz.name %}from {{class_module_map[rpm.property.type.name]}} import {{rpm.property.type.name}}RelatedFinder
{% endif %}
{% endfor %}


class {{rcm.clazz.name}}RelatedFinder({{rcm.clazz.name}}RelatedFinderBase, RelatedFinder):

{% if rcm.milestone_mapping %}
{% if has_bitemporal(rcm.milestone_mapping) %}
    __zz_milestoning_columns = BiTemporalColumns(Column('{{rcm.milestone_mapping._date_from.target.name}}', '{{rcm.milestone_mapping._date_from.target.type}}'),
                                                 Column('{{rcm.milestone_mapping._date_to.target.name}}', '{{rcm.milestone_mapping._date_to.target.type}}'),
                                                 Column('{{rcm.milestone_mapping._in.target.name}}', '{{rcm.milestone_mapping._in.target.type}}'),
                                                 Column('{{rcm.milestone_mapping._out.target.name}}', '{{rcm.milestone_mapping._out.target.type}}'){% if rcm.milestone_mapping.infinite_datetime %}, infinite_datetime='{{rcm.milestone_mapping.infinite_datetime}}'{% endif %})
{% elif has_business_date_and_processing(rcm.milestone_mapping) %}
    __zz_milestoning_columns = BusinessDateAndProcessingTemporalColumns(Column('{{rcm.milestone_mapping._date.target.name}}', '{{rcm.milestone_mapping._date.target.type}}'),
                                                                        Column('{{rcm.milestone_mapping._in.target.name}}', '{{rcm.milestone_mapping._in.target.type}}'),
                                                                        Column('{{rcm.milestone_mapping._out.target.name}}', '{{rcm.milestone_mapping._out.target.type}}'){% if rcm.milestone_mapping.infinite_datetime %}, infinite_datetime='{{rcm.milestone_mapping.infinite_datetime}}'{% endif %})
{% elif has_processing_temporal(rcm.milestone_mapping) %}
    __zz_milestoning_columns = ProcessingTemporalColumns(Column('{{rcm.milestone_mapping._in.target.name}}', '{{rcm.milestone_mapping._in.target.type}}'),
                                                         Column('{{rcm.milestone_mapping._out.target.name}}', '{{rcm.milestone_mapping._out.target.type}}'){% if rcm.milestone_mapping.infinite_datetime %}, infinite_datetime='{{rcm.milestone_mapping.infinite_datetime}}'{% endif %})
{% elif has_uni_business_temporal(rcm.milestone_mapping) %}
    __zz_milestoning_columns = SingleBusinessDateColumn(Column('{{rcm.milestone_mapping._date.target.name}}', '{{rcm.milestone_mapping._date.target.type}}'))
{% endif %}
    __table = MilestonedTable('{{table_qualified_name(rcm.property_mappings[0].target.table)}}', [], __zz_milestoning_columns)
{% else %}
    __table = Table('{{table_qualified_name(rcm.property_mappings[0].target.table)}}', [])
{% endif %}

    _column_map = {
{% for rpm in rcm.property_mappings %}
{% if is_primitive(rpm.property) %}
        '{{rpm.property.id}}': ('{{rpm.target.name}}', '{{rpm.target.type}}', '{{table_qualified_name(rpm.target.table)}}'),
{% endif %}
{% endfor %}
    }

    def __init__(self, relation_name: str, source: Column, target: Column, parent_join: JoinTreeNodeOperation = None, **kwargs):
        join = JoinOperation(relation_name, {{rcm.clazz.name}}RelatedFinder.__table, source, target)
        self.__node = JoinTreeNodeOperation(join, parent_join)
        RelatedFinder.__init__(self, self.__node)
{% for rpm in rcm.property_mappings %}
{% if is_primitive(rpm.property) %}
        self.__{{to_python_name(rpm.property)}} = {{rpm.property.type.name}}Attribute(relation_name.title() + ' {{display_name(rpm.property)}}', '{{rpm.target.name}}', '{{rpm.target.type}}', '{{table_qualified_name(rpm.target.table)}}', self.__node)
{% endif %}
{% endfor %}
{% for rpm in rcm.property_mappings %}
{% if not is_primitive(rpm.property) and rpm.property.type.name != rcm.clazz.name %}
        self.__{{to_python_name(rpm.property)}} = {{rpm.property.type.name}}RelatedFinder('{{rpm.property.name}}', Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'),Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'), self.__node)
{% endif %}
{% endfor %}
{% for source_rcm, rpm, assoc, reverse_name in reverse_assocs %}
        self.__{{reverse_name}} = None
{% endfor %}
        if kwargs:
            join.filter_kwargs = kwargs
            join.filter_column_map = {{rcm.clazz.name}}RelatedFinder._column_map

{% for rpm in rcm.property_mappings %}
{% if is_primitive(rpm.property) %}
    def {{to_python_name(rpm.property)}}(self) -> {{rpm.property.type.name}}Attribute:
{% if rpm.property.tagged_values['doc'] %}
        """{{display_name(rpm.property)}}
        {{rpm.property.tagged_values['doc'].value}}
        """
{% else %}
        """{{display_name(rpm.property)}}"""
{% endif %}
        return self.__{{to_python_name(rpm.property)}}

{% elif rpm.property.type.name != rcm.clazz.name %}
    def {{to_python_name(rpm.property)}}(self, **kwargs) -> {{rpm.property.type.name}}RelatedFinder:
{% if rpm.property.tagged_values['doc'] %}
        """{{display_name(rpm.property)}}
        {{rpm.property.tagged_values['doc'].value}}
        """
{% else %}
        """{{display_name(rpm.property)}}"""
{% endif %}
        if kwargs:
            return {{rpm.property.type.name}}RelatedFinder('{{rpm.property.name}}', Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'), Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'), self.__node, **kwargs)
        return self.__{{to_python_name(rpm.property)}}

{% endif %}
{% endfor %}
{% for source_rcm, rpm, assoc, reverse_name in reverse_assocs %}
    def {{reverse_name}}(self, **kwargs) -> {{source_rcm.clazz.name}}RelatedFinder:
        """{{source_rcm.clazz.name}}"""
        if kwargs:
            from {{class_module_map[source_rcm.clazz.name]}} import {{source_rcm.clazz.name}}RelatedFinder
            return {{source_rcm.clazz.name}}RelatedFinder('{{source_rcm.clazz.name}}', Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'), Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'), self.__node, **kwargs)
        if self.__{{reverse_name}} is None:
            from {{class_module_map[source_rcm.clazz.name]}} import {{source_rcm.clazz.name}}RelatedFinder
            self.__{{reverse_name}} = {{source_rcm.clazz.name}}RelatedFinder('{{source_rcm.clazz.name}}', Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'),Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'), self.__node)
        return self.__{{reverse_name}}

{% endfor %}

class {{rcm.clazz.name}}Finder({{rcm.clazz.name}}FinderBase):
{% if rcm.clazz.tagged_values['doc'] %}
    """{{rcm.clazz.tagged_values['doc'].value}}
    """
{% endif %}

{% if rcm.milestone_mapping %}
{% if has_bitemporal(rcm.milestone_mapping) %}
    __zz_milestoning_columns = BiTemporalColumns(Column('{{rcm.milestone_mapping._date_from.target.name}}', '{{rcm.milestone_mapping._date_from.target.type}}'),
                                                 Column('{{rcm.milestone_mapping._date_to.target.name}}', '{{rcm.milestone_mapping._date_to.target.type}}'),
                                                 Column('{{rcm.milestone_mapping._in.target.name}}', '{{rcm.milestone_mapping._in.target.type}}'),
                                                 Column('{{rcm.milestone_mapping._out.target.name}}', '{{rcm.milestone_mapping._out.target.type}}'){% if rcm.milestone_mapping.infinite_datetime %}, infinite_datetime='{{rcm.milestone_mapping.infinite_datetime}}'{% endif %})
{% elif has_business_date_and_processing(rcm.milestone_mapping) %}
    __zz_milestoning_columns = BusinessDateAndProcessingTemporalColumns(Column('{{rcm.milestone_mapping._date.target.name}}', '{{rcm.milestone_mapping._date.target.type}}'),
                                                                        Column('{{rcm.milestone_mapping._in.target.name}}', '{{rcm.milestone_mapping._in.target.type}}'),
                                                                        Column('{{rcm.milestone_mapping._out.target.name}}', '{{rcm.milestone_mapping._out.target.type}}'){% if rcm.milestone_mapping.infinite_datetime %}, infinite_datetime='{{rcm.milestone_mapping.infinite_datetime}}'{% endif %})
{% elif has_processing_temporal(rcm.milestone_mapping) %}
    __zz_milestoning_columns = ProcessingTemporalColumns(Column('{{rcm.milestone_mapping._in.target.name}}', '{{rcm.milestone_mapping._in.target.type}}'),
                                                         Column('{{rcm.milestone_mapping._out.target.name}}', '{{rcm.milestone_mapping._out.target.type}}'){% if rcm.milestone_mapping.infinite_datetime %}, infinite_datetime='{{rcm.milestone_mapping.infinite_datetime}}'{% endif %})
{% elif has_uni_business_temporal(rcm.milestone_mapping) %}
    __zz_milestoning_columns = SingleBusinessDateColumn(Column('{{rcm.milestone_mapping._date.target.name}}', '{{rcm.milestone_mapping._date.target.type}}'))
{% endif %}
    __table = MilestonedTable('{{table_qualified_name(rcm.property_mappings[0].target.table)}}', [], __zz_milestoning_columns)
{% else %}
    __table = Table('{{table_qualified_name(rcm.property_mappings[0].target.table)}}', [])
{% endif %}

{% for rpm in rcm.property_mappings %}
{% if is_primitive(rpm.property) %}
    __{{to_python_name(rpm.property)}} = {{rpm.property.type.name}}Attribute('{{display_name(rpm.property)}}', '{{rpm.target.name}}', '{{rpm.target.type}}', '{{table_qualified_name(rpm.target.table)}}')
{% endif %}
{% endfor %}
{% for rpm in rcm.property_mappings %}
{% if not is_primitive(rpm.property) %}
    __{{to_python_name(rpm.property)}} = {{rpm.property.type.name}}RelatedFinder('{{rpm.property.name}}', Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'),Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'))
{% endif %}
{% endfor %}
{% if reverse_assocs %}

    def __init__(self):
{% for source_rcm, rpm, assoc, reverse_name in reverse_assocs %}
        self.__{{reverse_name}} = None
{% endfor %}

{% endif %}
{% for rpm in rcm.property_mappings %}
{% if is_primitive(rpm.property) %}
    def {{to_python_name(rpm.property)}}(self) -> {{rpm.property.type.name}}Attribute:
{% if rpm.property.tagged_values['doc'] %}
        """{{display_name(rpm.property)}}
        {{rpm.property.tagged_values['doc'].value}}
        """
{% else %}
        """{{display_name(rpm.property)}}"""
{% endif %}
        return {{rcm.clazz.name}}Finder.__{{to_python_name(rpm.property)}}

{% else %}
    def {{to_python_name(rpm.property)}}(self, **kwargs) -> {{rpm.property.type.name}}RelatedFinder:
{% if rpm.property.tagged_values['doc'] %}
        """{{display_name(rpm.property)}}
        {{rpm.property.tagged_values['doc'].value}}
        """
{% else %}
        """{{display_name(rpm.property)}}"""
{% endif %}
        if kwargs:
            return {{rpm.property.type.name}}RelatedFinder('{{rpm.property.name}}', Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'), Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'), None, **kwargs)
        return {{rcm.clazz.name}}Finder.__{{to_python_name(rpm.property)}}

{% endif %}
{% endfor %}
{% for source_rcm, rpm, assoc, reverse_name in reverse_assocs %}
    def {{reverse_name}}(self, **kwargs) -> {{source_rcm.clazz.name}}RelatedFinder:
        """{{source_rcm.clazz.name}}"""
        if kwargs:
            from {{class_module_map[source_rcm.clazz.name]}} import {{source_rcm.clazz.name}}RelatedFinder
            return {{source_rcm.clazz.name}}RelatedFinder('{{source_rcm.clazz.name}}', Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'), Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'), None, **kwargs)
        if self.__{{reverse_name}} is None:
            from {{class_module_map[source_rcm.clazz.name]}} import {{source_rcm.clazz.name}}RelatedFinder
            self.__{{reverse_name}} = {{source_rcm.clazz.name}}RelatedFinder('{{source_rcm.clazz.name}}', Column('{{rpm.target.rhs.name}}', '{{rpm.target.rhs.type}}', '{{table_qualified_name(rpm.target.rhs.table)}}'),Column('{{rpm.target.lhs.name}}', '{{rpm.target.lhs.type}}', '{{table_qualified_name(rpm.target.lhs.table)}}'))
        return self.__{{reverse_name}}

{% endfor %}
    def count(self) -> CountAllOperation:
        """Count all rows in the underlying table."""
        return CountAllOperation('{{table_qualified_name(rcm.property_mappings[0].target.table)}}')

    def find_all(self,
                 business_date: Union[datetime.date, str],
                 processing_valid_at: Union[datetime.datetime, str],
                 display_columns: list[Attribute],
                 filter_op: Operation = NoOperation()) -> FinderResult:
        """Returns data matching the optional filter."""
{% if rcm.milestone_mapping %}
{% if has_bitemporal(rcm.milestone_mapping) %}
        return convert_inputs_and_select(business_date, processing_valid_at, display_columns, {{rcm.clazz.name}}Finder.__table, filter_op)
{% elif has_business_date_and_processing(rcm.milestone_mapping) %}
        return convert_inputs_and_select(business_date, processing_valid_at, display_columns, {{rcm.clazz.name}}Finder.__table, filter_op)
{% elif has_processing_temporal(rcm.milestone_mapping) %}
        return convert_inputs_and_select(None, processing_valid_at, display_columns, {{rcm.clazz.name}}Finder.__table, filter_op)
{% elif has_uni_business_temporal(rcm.milestone_mapping) %}
        return convert_inputs_and_select(business_date, processing_valid_at, display_columns, {{rcm.clazz.name}}Finder.__table, filter_op)
{% endif %}
{% else %}
        return convert_inputs_and_select(None, None, display_columns, {{rcm.clazz.name}}Finder.__table, filter_op)
{% endif %}
