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

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)})"


@runtime_checkable
class EntityReference(Record, Protocol):
    """An entity reference instance allows accessing data by name or index.

    Attributes:
        id EntityReferenceIdentifier: identifies the entity reference
    """

    id: EntityReferenceIdentifier

    def as_dict(self) -> dict[str, Any]:
        """Return a dictionary containing the reference's attribute names and values."""
        pass


@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]
