import abc
from Models.UserModel import UserModel
from Matching.MetaMetrics import MetaMetric
from datetime import datetime, timedelta


class Metric(MetaMetric):
    """
    Base-Class for leaf metrics
    """

    def __init__(self, user):
        super().__init__()
        self.user: UserModel = user

    @abc.abstractmethod
    def get_score(self, other_user):
        """returns the score the metric assigns between the
        metrics user and the other user passed as an argument"""

    def reset_user(self, user):
        self.user = user


class SameProjectMetric(Metric):
    def __init__(self, user: UserModel):
        super().__init__(user)

    def get_score(self, other_user):
        common_projects = 1 + len(self.user.common_projects(other_user))
        uncommon_projects = 1 + 0.1 * len(set(self.user.personal_information.projects) ^
                                          set(other_user.personal_information.projects))
        return common_projects / uncommon_projects


class DifferentProjectMetric(Metric):
    def __init__(self, user: UserModel):
        super().__init__(user)

    def get_score(self, other_user):
        common_projects = 1 + 0.1 * len(self.user.common_projects(other_user))
        uncommon_projects = 1 + len(set(self.user.personal_information.projects) ^
                                    set(other_user.personal_information.projects))
        return uncommon_projects / common_projects


def get_semester_diff(user, other_user):
    """
    returns semester diff from the viewpoint of the user:
    negative integer if other user is an earlier semester
    positive integer if other user is an higher semester
    """
    time_diff = other_user.personal_information.matriculation_date - user.personal_information.matriculation_date
    semester_diff = int(time_diff.days / (364.25 / 2))
    return semester_diff


class SameAgeGroupMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        semester_diff = get_semester_diff(self.user, other_user)

        score = 10 - abs(semester_diff)
        if score < 0:
            score = 0
        return score


class HigherAgeGroupMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        semester_diff = get_semester_diff(self.user, other_user)

        score = 5
        score += (semester_diff / 2)
        if score > 10:
            score = 10
        if score < 0:
            score = 0
        return score


class LowerAgeGroupMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        semester_diff = get_semester_diff(self.user, other_user)

        score = 5
        score -= (semester_diff / 2)
        if score > 10:
            score = 10
        if score < 0:
            score = 0
        return score


def get_same_percentage(set_one, set_two):
    all_interests = set_one | set_two
    if not all_interests:
        return 1
    common_interests = set_one & set_two
    return len(common_interests) / len(all_interests)


class SameInterestsMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        if (interests := self.user.personal_information.interest_list) is not None:
            own_interests = set(interests)
        else:
            own_interests = set([])
        if (interests := other_user.personal_information.interest_list) is not None:
            other_interests = set(interests)
        else:
            other_interests = set([])
        # map percentage to 1 to 10 range
        return get_same_percentage(own_interests, other_interests) * 10


class DifferentInterestsMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        if (interests := self.user.personal_information.interest_list) is not None:
            own_interests = set(interests)
        else:
            own_interests = set([])
        if (interests := other_user.personal_information.interest_list) is not None:
            other_interests = set(interests)
        else:
            other_interests = set([])
        # map inverse percentage from one to 10 range
        return (1 - get_same_percentage(own_interests, other_interests)) * 10


# userdata independent metrics
class LikedBackMetric(Metric):
    def __init__(self, user: UserModel):
        super().__init__(user)

    def get_score(self, other_user):
        return 1 if self.user in other_user.likes else 0


class HasMatchesMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        match_count = len(other_user.matches)
        if match_count > 30:
            reduce_factor = 1 - (match_count - 30) * 0.05
            if reduce_factor < 0.6:
                reduce_factor = 0.6
        else:
            reduce_factor = 1
        return reduce_factor


class IsInactiveMetric(Metric):
    def __init__(self, user):
        super().__init__(user)

    def get_score(self, other_user):
        rating = min((datetime.utcnow() - other_user.last_active).days, 100)
        if rating > 40:
            return 1-(rating/100)
        return 1


class DontCaraMetric(Metric):
    def get_score(self, user):
        return 0
