from dataclasses import dataclass
from typing import Union, Optional

from analogai.foundation.common.enums.relationship_type import RelationshipType
from analogai.foundation.common.utils.relation_utils import normalize_relation_value


@dataclass(frozen=True)
class Relation:
    value: str
    _relationship_type: Optional[RelationshipType]

    @classmethod
    def from_type(cls, relationship_type: RelationshipType) -> 'Relation':
        return cls(
            value=relationship_type.value,
            _relationship_type=relationship_type
        )

    @classmethod
    def from_string(cls, value: str) -> 'Relation':
        if not value or value.strip() == "":
            raise ValueError("relation cannot be empty")
        cleaned_value = value.strip()
        for rel_type in RelationshipType:
            if rel_type.value == cleaned_value:
                return cls(value=cleaned_value, _relationship_type=rel_type)
        normalized_value = normalize_relation_value(cleaned_value)
        if normalized_value != cleaned_value:
            for rel_type in RelationshipType:
                if rel_type.value == normalized_value:
                    return cls(value=cleaned_value, _relationship_type=rel_type)
        return cls(value=cleaned_value, _relationship_type=None)

    @classmethod
    def create(cls, value: Union[str, RelationshipType]) -> 'Relation':
        if isinstance(value, RelationshipType):
            return cls.from_type(value)
        return cls.from_string(value)

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Relation):
            return self.value == other.value
        raise NotImplementedError(f"Cannot compare Relation with {type(other)}")

    def is_of_type(self, relationship_type: RelationshipType) -> bool:
        if self._relationship_type is not None:
            return self._relationship_type == relationship_type
        return self.value == relationship_type.value

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

    def __str__(self):
        return self.value
