from typing import Optional, Union

from analogai.foundation.common.enums.relationship_type import RelationshipType
from analogai.foundation.core.value_objects.relation import Relation


def to_storage_value(relation: Union[Relation, RelationshipType]) -> str:
    if relation is None:
        return ""
    if isinstance(relation, Relation):
        return relation.value
    if isinstance(relation, RelationshipType):
        return relation.value
    raise TypeError("relation must be a Relation or RelationshipType")


def to_relationship_type(relation: Union[Relation, RelationshipType, str]) -> Optional[RelationshipType]:
    if not relation:
        return None
    try:
        rel_type = relation._relationship_type
        if rel_type is not None:
            return rel_type
    except AttributeError:
        pass
    try:
        return RelationshipType(relation)
    except (ValueError, TypeError):
        pass
    try:
        value = relation.value
    except AttributeError:
        value = relation
    for rel_type in RelationshipType:
        if rel_type.value == str(value):
            return rel_type
    return None


_RELATIONSHIP_TYPE_PRIORITY = {
    (RelationshipType.IS_A, None): "minor",
    (RelationshipType.SYNONYM, None): "minor",
    (None, RelationshipType.IS_A): "major",
    (None, RelationshipType.SYNONYM): "major",
}

_RELATIONSHIP_TYPE_SELECTIONS = {
    (RelationshipType.CAUSES, RelationshipType.CAUSES): RelationshipType.CAUSES,
    (RelationshipType.RELATED_TO, RelationshipType.RELATED_TO): RelationshipType.RELATED_TO,
    (RelationshipType.FORM_OF, RelationshipType.FORM_OF): RelationshipType.FORM_OF,
    (RelationshipType.AT_LOCATION, RelationshipType.AT_LOCATION): RelationshipType.AT_LOCATION,
    (RelationshipType.HAS_SUBEVENT, RelationshipType.HAS_SUBEVENT): RelationshipType.HAS_SUBEVENT,
    (RelationshipType.HAS_SUBEVENT, RelationshipType.HAS_FIRST_SUBEVENT): RelationshipType.HAS_SUBEVENT,
    (RelationshipType.HAS_SUBEVENT, RelationshipType.HAS_LAST_SUBEVENT): RelationshipType.HAS_SUBEVENT,
    (RelationshipType.HAS_FIRST_SUBEVENT, RelationshipType.HAS_FIRST_SUBEVENT): RelationshipType.HAS_FIRST_SUBEVENT,
    (RelationshipType.HAS_LAST_SUBEVENT, RelationshipType.HAS_LAST_SUBEVENT): RelationshipType.HAS_LAST_SUBEVENT,
    (RelationshipType.HAS_PREREQUISITE, RelationshipType.HAS_PREREQUISITE): RelationshipType.HAS_PREREQUISITE,
    (RelationshipType.SYNONYM, RelationshipType.SYNONYM): RelationshipType.SYNONYM,
    (RelationshipType.ANTONYM, RelationshipType.ANTONYM): RelationshipType.SYNONYM,
    (RelationshipType.SYMBOL_OF, RelationshipType.SYMBOL_OF): RelationshipType.SYMBOL_OF,
    (RelationshipType.LOCATED_NEAR, RelationshipType.LOCATED_NEAR): RelationshipType.LOCATED_NEAR,
    (RelationshipType.SIMILAR_TO, RelationshipType.SIMILAR_TO): RelationshipType.SIMILAR_TO,
    (RelationshipType.CAUSES_DESIRE, RelationshipType.CAUSES_DESIRE): RelationshipType.CAUSES_DESIRE,
    (RelationshipType.MADE_OF, RelationshipType.MADE_OF): RelationshipType.MADE_OF,
}


def select_relationship_type(
    major_premise_type: RelationshipType,
    minor_premise_type: RelationshipType,
) -> Optional[RelationshipType]:
    priority = _RELATIONSHIP_TYPE_PRIORITY.get((major_premise_type, None))
    if priority == "minor":
        return minor_premise_type
    priority = _RELATIONSHIP_TYPE_PRIORITY.get((None, minor_premise_type))
    if priority == "major":
        return major_premise_type
    return _RELATIONSHIP_TYPE_SELECTIONS.get((major_premise_type, minor_premise_type))


def to_relation(value: RelationshipType) -> Relation:
    return Relation.from_type(value)


def _relation_from_value(value: Union[Relation, RelationshipType, str]) -> Optional[Relation]:
    if value is None:
        return None
    if isinstance(value, Relation):
        return value
    if isinstance(value, RelationshipType):
        return Relation.from_type(value)
    return Relation.from_string(str(value))


def select_relation(
    major_relation: Union[Relation, RelationshipType, str],
    minor_relation: Union[Relation, RelationshipType, str],
) -> Optional[Relation]:
    major_type = to_relationship_type(major_relation)
    minor_type = to_relationship_type(minor_relation)

    priority = _RELATIONSHIP_TYPE_PRIORITY.get((major_type, None))
    if priority == "minor":
        return _relation_from_value(minor_relation)
    priority = _RELATIONSHIP_TYPE_PRIORITY.get((None, minor_type))
    if priority == "major":
        return _relation_from_value(major_relation)

    if major_type is None or minor_type is None:
        return None

    selected = _RELATIONSHIP_TYPE_SELECTIONS.get((major_type, minor_type))
    if not selected:
        return None
    return Relation.from_type(selected)
