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

from sqlalchemy.orm import InstrumentedAttribute

from .move import descend, ascend


class ProxyStep(NamedTuple):
    name: str
    local_column_to_remote: dict[str, str]
    alias: str = None
    reverse: bool = False


@dataclass
class AttributeProxy:
    name: str
    attribute: InstrumentedAttribute
    path: list[ProxyStep] = dataclasses.field(default_factory=list)
    alias: Optional[str] = None

    def descend(self, alias: Optional[str] = None) -> 'EntityProxy':
        entity_proxy = descend(self.attribute, alias=alias)
        return entity_proxy.prepend_steps(self.path)

    def ascend(self, alias: Optional[str]) -> 'EntityProxy':
        entity_proxy = ascend(self.attribute, alias=alias)
        return entity_proxy.prepend_steps(self.path)

    def label(self, alias: Optional[str]):
        return AttributeProxy(name=self.name, attribute=self.attribute, path=self.path[:], alias=alias)


class EntityProxy:
    def __init__(self, entity, path: list[ProxyStep], alias: Optional[str] = None):
        self._path = path
        self._entity = entity
        if alias:
            last_step_as_dict = self._path[-1]._asdict()
            last_step_as_dict["alias"] = alias
            self._path[-1] = ProxyStep(**last_step_as_dict)
        # self._alias = alias
        # print("init EntityProxy", self)

    def get_entity(self):
        return self._entity

    def get_path(self) -> list[ProxyStep]:
        return self._path

    def get_alias(self) -> Optional[str]:
        return self._alias

    def prepend_steps(self, steps) -> 'EntityProxy':
        # print("prepend_steps", steps, self._path)
        return EntityProxy(self._entity, steps + self._path)

    def __getattr__(self, item: str):
        result = getattr(self._entity, item)
        if isinstance(result, InstrumentedAttribute):
            return AttributeProxy(item, result, self._path[:])
        return object.__getattribute__(self, item)

    def __str__(self):
        cls = type(self)
        return f'{cls.__name__}(entity={self._entity}, path={self._path})'

    def label(self, alias: Optional[str]):
        return EntityProxy(entity=self._entity, path=self._path[:], alias=alias)
