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

from entity_read import sql
from entity_read.entity import Entity
from .expression import Expression
from .query_token import QueryToken
from .order import Order
from .subquery import prepare_tokens
from ._eval_to_selector import eval_to_selector


@dataclass(frozen=True, kw_only=True, repr=False)
class Query(QueryToken):
    # entity: str
    attrs: list[Expression] | Expression = field(default_factory=list)
    vars: dict[str, Expression] = field(default_factory=dict)
    filters: list[Expression] = field(default_factory=list)
    searches: list[Expression] = field(default_factory=list)
    orders: list[Order] = field(default_factory=list)
    limit: int | None = None
    offset: int | None = None
    is_scalar: bool | None = None

    def __post_init__(self):
        if self.is_scalar is None:
            object.__setattr__(self, 'is_scalar', not isinstance(self.attrs, list))

    def shortcut(self) -> str:
        # parts = [self.entity]
        parts = []
        parts += prepare_tokens('attrs', self.attrs)
        parts += [f'vars={",".join([f"{k}={v.shortcut()}" for k, v in self.vars.items()])}'] if self.vars else []
        parts += prepare_tokens('filters', self.filters)
        parts += prepare_tokens('searches', self.searches)
        parts += prepare_tokens('orders', self.orders)
        parts += [f'limit={self.limit}'] if self.limit else []
        parts += [f'offset={self.offset}'] if self.offset else []
        parts += [f'is_scalar={self.is_scalar}'] if self.is_scalar else []
        # parts += [f'attrs={[attr.shortcut() for attr in self.attrs]}'] if self.attrs else []
        # parts += [f'vars={[f"{key}={val.shortcut()}" for key, val in self.vars.items()]}'] if self.vars else []
        # parts += [f'filters={[attr.shortcut() for attr in self.filters]}'] if self.filters else []
        # parts += [f'searches={[attr.shortcut() for attr in self.searches]}'] if self.searches else []
        # parts += [f'orders={[attr.shortcut() for attr in self.orders]}'] if self.orders else []
        return f'Query({",".join(parts)})'

    def eval(self, entity_type: type[Entity]) -> sql.lower.LowerSelector:
        return eval_to_selector(
            entity_type=entity_type, local_column_to_remote={},
            attrs=self.attrs, vars_=self.vars, filters=self.filters, searches=self.searches,
            orders=self.orders, limit=self.limit, offset=self.offset, is_scalar=self.is_scalar
        )

        # variables = {}
        # for key, val in self.vars.items():
        #     variables[key] = val.eval(entity_type, variables)
        #
        # where = [attr.eval(entity_type, variables) for attr in self.filters]
        # if self.searches:
        #     where.append(sql.inter.FunctionOr(args=tuple(attr.eval(entity_type, variables) for attr in self.searches)))
        #
        # result_name_to_selectable = {}
        # step_to_sub_selector = {}
        # for attr in self.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)
        #         step_to_sub_selector[attr.over.eval(entity_type, variables).get_name()] = node
        #     else:
        #         raise TypeError("goy unexpected type")
        #
        # result = sql.lower.LowerSelector(
        #     table_name=entity_type.get_table_name(), result_name_to_selectable=result_name_to_selectable,
        #     where=where, is_hidden=False, order=[attr.eval(entity_type, variables) for attr in self.order],
        #     limit=self.limit, offset=self.offset,
        #     local_column_to_remote={}, step_to_sub_selector=step_to_sub_selector
        # )
        # return result
