#  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 entity_read.sql.atoms import Selectable

from ._eval_to_selector import eval_to_selector
# from .variable import Variable
from .query_token import QueryToken
from .reference import Reference
from .expression import Expression
from .order import Order
from .relation import Relation


@dataclass(frozen=True, kw_only=True, repr=False)
class SubQuery(Expression):
    over: Relation | Reference
    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

    def eval(self, entity_type: type[Entity], variables: dict[str, Selectable]) -> sql.lower.LowerSelector:
        is_scalar = not isinstance(self.over, Relation) or self.over.get_is_scalar(entity_type)
        result = eval_to_selector(
            entity_type=self.over.get_related_entity(entity_type),
            local_column_to_remote=self.over.get_remote_column_name_to_local(entity_type),  # relatively to child
            my_step=self.over.key, rename=self.shortcut(), is_scalar=is_scalar,
            attrs=self.attrs, vars_=self.vars, filters=self.filters, searches=self.searches,
            orders=self.orders, limit=self.limit, offset=self.offset
        )
        return result

    def shortcut(self) -> str:
        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 []
        return f'{self.over.shortcut()}.subquery({",".join(parts)})'


def prepare_tokens(key: str, vals: list[QueryToken] | None) -> list[str]:
    if isinstance(vals, list):
        return [f'{key}=[{",".join([val.shortcut() for val in vals])}]'] if vals else []
    elif isinstance(vals, QueryToken):
        return [f'{key}={vals.shortcut()}']
    else:
        return []
