import abc


class MetaMetric(metaclass=abc.ABCMeta):
    def __init__(self):
        pass

    @abc.abstractmethod
    def get_score(self, user):
        """matching score of a metric"""

    def __add__(self, other):
        if isinstance(other, int) or isinstance(other, float):
            return AddNumberMetric(self, other)
        return SumMetric([self, other])

    def __mul__(self, other):
        if isinstance(other, int) or isinstance(other, float):
            return MulNumberMetric(self, other)
        return ProductMetric([self, other])

    @abc.abstractmethod
    def reset_user(self, new_self_user):
        """change up the own user"""


class SumMetric(MetaMetric):
    def __init__(self, metric_list):
        super().__init__()
        self.metric_list = metric_list

    def reset_user(self, new_self_user):
        for metric in self.metric_list:
            metric.reset_user(new_self_user)

    def get_score(self, user):
        out_sum = 0
        for metric in self.metric_list:
            out_sum += metric.get_score(user)
        return out_sum


class AddNumberMetric(MetaMetric):
    def __init__(self, wrapped_metric, addition):
        super().__init__()
        self.wrapped_metric = wrapped_metric
        self.addition = addition

    def get_score(self, user):
        return self.wrapped_metric.get_score(user) + self.addition

    def reset_user(self, new_self_user):
        self.wrapped_metric.reset_user(new_self_user)


class ProductMetric(MetaMetric):
    def __init__(self, metric_list):
        super().__init__()
        self.metric_list = metric_list

    def get_score(self, user):
        out_product = 1
        for metric in self.metric_list:
            out_product *= metric.get_score(user)
        return out_product

    def reset_user(self, new_self_user):
        for metric in self.metric_list:
            metric.reset_user(new_self_user)


class MulNumberMetric(MetaMetric):
    def __init__(self, wrapped_metric, multiplier):
        super().__init__()
        self.wrapped_metric = wrapped_metric
        self.multiplier = multiplier

    def get_score(self, user):
        return self.wrapped_metric.get_score(user) * self.multiplier

    def reset_user(self, new_self_user):
        self.wrapped_metric.reset_user(new_self_user)
