import itertools
from dataclasses import dataclass
from typing import Hashable, Iterable, Sized, Protocol, Callable, runtime_checkable

from matchescu.data._record import Record as DataRecord
from matchescu.typing._data import Record


@dataclass(frozen=True, eq=True)
class EntityReferenceIdentifier:
    """Identifies an entity reference.

    Attributes:
        :label Hashable: unique label identifying an entity reference within a collection
        :source str: a string describing where the entity reference originated
    """

    label: Hashable
    source: str

    def __repr__(self) -> str:
        return f"ref_id{{{self.source},{repr(self.label)}}}"

    def __str__(self) -> str:
        return f"{self.source}({str(self.label)})"


class EntityReference(DataRecord):
    id: EntityReferenceIdentifier

    def __init__(self, identifier: EntityReferenceIdentifier, value: Iterable):
        super().__init__(value)
        self.id = identifier

    def __eq__(self, __value):
        if not isinstance(__value, EntityReference):
            return False
        return __value.id == self.id

    def __ne__(self, __value):
        return not self.__eq__(__value)

    def __repr__(self):
        return "EntityReference(id={})".format(repr(self.id))

    def __hash__(self):
        return hash(self.id)

    def __dir__(self):
        return list(itertools.chain(super().__dir__(), self._attr_names.keys()))

    def as_dict(self) -> dict:
        return {
            attr_name: self._attr_values[attr_idx]
            for attr_name, attr_idx in self._attr_names.items()
        }


@runtime_checkable
class EntityProfile(Iterable[EntityReference], Sized, Protocol):
    """Entity profiles are entity references aggregating other entity references.

    Concrete entity profiles have different representations according to the
    entity resolution model being used. However, all entity profiles can be
    treated as 'compound' entity references obtained by aggregating existing
    'atomic' entity references.

    Just like any other entity reference, an entity profile has a unique ID.
    However, because they were generated by the entity resolution process, they
    have no source.
    """


EntityReferenceIdFactory = Callable[[Iterable[Record]], EntityReferenceIdentifier]
