from datetime import datetime
from typing import Union, Callable, Any

from api_graphql.abc.query_abc import QueryABC
from core.database.abc.data_access_object_abc import DataAccessObjectABC


class DbHistoryModelQueryABC(QueryABC):

    def __init__(self, name: str = __name__):
        QueryABC.__init__(self, f"{name}History")

        self.set_field("id", lambda x, *_: x.id)
        self.set_field("deleted", lambda x, *_: x.deleted)
        self.set_field("editor", self._resolve_editor)
        self.set_field("created", lambda x, *_: x.created)
        self.set_field("updated", lambda x, *_: x.updated)

    @staticmethod
    async def _resolve_editor(x, *_):
        editor = await x.editor
        return editor.username if editor else None

    @staticmethod
    async def _resolve_foreign_history(
        updated: datetime,
        obj_ident: Union[str, int],
        join_dao: DataAccessObjectABC,
        foreign_dao: DataAccessObjectABC,
        foreign_join_key: Callable[[Any], Any],
        obj_key="id",
        *_,
    ):
        foreign_history = sorted(
            [
                *await join_dao.find_by(
                    [
                        {obj_key: obj_ident},
                        {"updated": {"lessOrEqual": updated}},
                    ]
                ),
                *await join_dao.get_history(
                    obj_ident,
                    by_key=obj_key,
                    until=updated,
                ),
            ],
            key=lambda x: x.updated,
        )

        foreign_ids = set()
        for foreign in foreign_history:
            if not foreign.deleted:
                foreign_ids.add(foreign_join_key(foreign))
                continue

            foreign_ids.discard(foreign_join_key(foreign))

        return [await foreign_dao.get_by_id(x) for x in sorted(foreign_ids)]
