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

import sqlalchemy.orm

from entity_read.entity import Entity
from .order import Order
from .expression import Expression
from .query_token import QueryToken


@dataclass(frozen=True, slots=True, kw_only=True, repr=False)
class Relation(QueryToken):
    key: str

    def get_related_entity(self, entity_type: type[Entity]) -> type[Entity]:
        relation = self._get_relation(entity_type)
        result = relation.entity.entity
        return result

    def get_local_column_name_to_remote(self, entity_type: type[Entity]) -> dict[str, str]:
        relation = self._get_relation(entity_type)
        return {local_column.name: remote_column.name for local_column, remote_column in relation.local_remote_pairs}

    def get_remote_column_name_to_local(self, entity_type: type[Entity]) -> dict[str, str]:
        relation = self._get_relation(entity_type)
        return {remote_column.name: local_column.name for local_column, remote_column in relation.local_remote_pairs}

    def get_is_scalar(self, entity_type: type[Entity]) -> bool:
        relation = self._get_relation(entity_type)
        return not relation.uselist

    def _get_relation(self, entity_type: type[Entity]) -> sqlalchemy.orm.Relationship:
        if (relation := entity_type.get_key_to_relation().get(self.key)) is None:
            raise KeyError(f"Not found relation {self.key!r} in {entity_type.__name__}"
                           f"({entity_type.get_relation_names()})")
        return relation

    def subquery(
            self,
            attrs: list[Expression] | None = None,
            vars: dict[str, Expression] | None = None,
            filters: list[Expression] | None = None,
            searches: list[Expression] | None = None,
            orders: list[Order] | None = None,
            limit: int | None = None,
            offset: int | None = None,
    ):
        from .subquery import SubQuery
        return SubQuery(
            over=self, attrs=attrs or [], vars=vars or {},
            filters=filters or [], searches=searches or [],
            orders=orders or [], limit=limit, offset=offset
        )

    def exists(
            self,
            vars: dict[str, Expression] | None = None,
            filters: list[Expression] | None = None,
            searches: list[Expression] | None = None,
    ):
        from .function import Function
        subquery = self.subquery(vars=vars, filters=filters, searches=searches)
        return Function(key="exists", args=[subquery])

    def shortcut(self) -> str:
        return f"rel.{self.key}"
