""" TODO re-implement this later, for now, filters should just be functions
"""

from abc import ABC, abstractmethod
from typing import Callable

from chemrecon.schema.db_object import SourceEntry
from chemrecon.core.id_types import IdentifierType
from chemrecon.schema.db_object import Entry, Relation
from chemrecon.schema.enums import SourceDatabase


class EntryFilter[T: Entry](ABC):
    """ Base class for filters. """
    entry_type: type[Entry]
    allowed_id_types: set[IdentifierType]
    has_id_type_field: bool

    @abstractmethod
    def __call__(self, entry: T) -> bool:
        # Base implementation
        return True

class RelationFilter[T1: Entry, T2: Entry](ABC):
    """ Base class for filters. """
    relation_type: type[Relation[T1, T2]]
    allowed_sources: set[SourceDatabase]
    has_src_field: bool

    def __init__(self, relation_type: type[Relation[T1, T2]]):
        self.relation_type = relation_type

    @abstractmethod
    def __call__(self, relation: Relation[T1, T2]) -> bool:
        # Base implementation
        return True

class EntryFilterIdType[T: SourceEntry](EntryFilter[T]):
    """
    """
    allowed_id_types: set[IdentifierType]
    # TODO

    def __init__(self, allowed_id_types: set[IdentifierType]):
        self.allowed_id_types = allowed_id_types
        raise NotImplementedError()

    def __call__(self, entry: T) -> bool:
        raise NotImplementedError()

# TODO similar to EntryFilterIdType, but for source


class EntryFilterProcedure[T: Entry](EntryFilter):
    """ Allows custom filtering on the application level in addition to type filtering.
    """
    procedure: Callable[[T], bool]

    def __init__(self, filter_proc: Callable[[T], bool]):
        self.procedure = filter_proc
        super().__init__()

    def __call__(self, entry: T) -> bool:
        if super().__call__(entry):
            return self.procedure(entry)
        else:
            return False



class RelationFilterProcedure[T1: Entry, T2: Entry](RelationFilter):
    """ Allows custom filtering on the application level in addition to source
    """
    procedure: Callable[[Relation[T1, T2]], bool]

    def __call__(self, relation: Relation[T1, T2]) -> bool:
        if super().__call__(relation):
            return self.procedure(relation)
        else:
            return False

    def __init__(
        self,
        relation_type: type[Relation[T1, T2]],
        filter_proc: Callable[[Relation[T1, T2]], bool]
    ):
        super().__init__(relation_type)
        self.procedure = filter_proc
