#  Copyright (C) 2023
#  ABM, Moscow
#
#  UNPUBLISHED PROPRIETARY MATERIAL.
#  ALL RIGHTS RESERVED.
#
#  Authors: Mike Orlov <m.orlov@abm-jsc.ru>
import typing

from entity_read import sql
from entity_read.entity import Entity

from .expression import Expression
from .order import Order
from .relation import Relation

if typing.TYPE_CHECKING:
    from .subquery import SubQuery


def eval_to_selector(
        entity_type: type[Entity], local_column_to_remote: dict[str, str],
        attrs: list[Expression], vars_: dict[str, Expression], filters: list[Expression], searches: list[Expression],
        orders: list[Order], limit: int | None, offset: int | None,
        my_step: str | None = None, rename: str | None = None, is_scalar: bool = False
) -> sql.lower.LowerSelector:
    from .subquery import SubQuery
    variables = {}
    for key, val in vars_.items():
        variables[key] = val.eval(entity_type, variables)

    where = [attr.eval(entity_type, variables) for attr in filters]
    if searches:
        where.append(sql.inter.FunctionOr(args=tuple(attr.eval(entity_type, variables) for attr in searches)))

    result_name_to_selectable = {}
    step_to_sub_selector = {}
    solo_result = None
    if isinstance(attrs, Expression):
        node = attrs.eval(entity_type, variables)
        if isinstance(node, sql.atoms.Selectable | sql.lower.LowerSelector):
            solo_result = node
        else:
            raise TypeError("got unexpected type")
    else:
        for attr in attrs:
            node = attr.eval(entity_type, variables)
            if isinstance(node, sql.atoms.Selectable):
                result_name_to_selectable[node.get_name()] = node
            elif isinstance(node, sql.lower.LowerSelector):
                assert isinstance(attr, SubQuery)
                if isinstance(attr.over, Relation):
                    step_to_sub_selector[attr.over.key] = node
                else:
                    step_to_sub_selector[f'ref_{attr.over.key}'] = node
            else:
                raise TypeError("got unexpected type")

    for key, var in variables.items():
        if isinstance(var, sql.lower.LowerSelector):
            assert key not in step_to_sub_selector
            step_to_sub_selector[key] = var
            var.is_hidden = True

    result = sql.lower.LowerSelector(
        table_name=entity_type.get_table_name(), schema_name=entity_type.get_table_schema_name(),
        result_name_to_selectable=result_name_to_selectable,
        where=where, is_hidden=False, order=[order.eval(entity_type, variables) for order in orders],
        limit=limit, offset=offset, my_step=my_step, rename=rename, is_scalar=is_scalar,
        local_column_to_remote=local_column_to_remote, step_to_sub_selector=step_to_sub_selector,
        solo_result=solo_result
    )
    return result
