from django.core.cache import cache
from rest_framework import serializers

from account.models import User
from achievement.models import Achievement, UserAchievement


def get_active_user_count():
    """获得率的分母：未禁用用户总数，缓存 1 小时。

    不用"有过提交的用户数"这类动态口径：分母波动会让同一个成就的获得率
    忽高忽低，学生会当成 bug。
    """
    count = cache.get("achievement_active_user_count")
    if count is None:
        count = User.objects.filter(is_disabled=False).count()
        cache.set("achievement_active_user_count", count, 3600)
    return count


class AchievementSerializer(serializers.ModelSerializer):
    unlocked = serializers.BooleanField(read_only=True)
    unlock_time = serializers.DateTimeField(read_only=True, allow_null=True)
    backfilled = serializers.BooleanField(read_only=True)
    progress = serializers.IntegerField(read_only=True, allow_null=True)
    unlock_rate = serializers.SerializerMethodField()
    name = serializers.SerializerMethodField()
    description = serializers.SerializerMethodField()
    icon = serializers.SerializerMethodField()
    # 条件三件套也必须跟着遮掉：只遮名称和描述、却明文下发
    # metric/operator/threshold，学生打开 DevTools 就知道"凌晨提交 10 次"，
    # 隐藏成就的意义全部作废
    metric = serializers.SerializerMethodField()
    operator = serializers.SerializerMethodField()
    threshold = serializers.SerializerMethodField()

    class Meta:
        model = Achievement
        fields = (
            "id",
            "name",
            "description",
            "icon",
            "rarity",
            "hidden",
            "metric",
            "operator",
            "threshold",
            "unlocked",
            "unlock_time",
            "backfilled",
            "progress",
            "unlock_rate",
        )

    def _masked(self, obj):
        return obj.hidden and not getattr(obj, "unlocked", False)

    def get_name(self, obj):
        return "???" if self._masked(obj) else obj.name

    def get_description(self, obj):
        return "达成条件保密" if self._masked(obj) else obj.description

    def get_icon(self, obj):
        # icon 存的是 iconify 图标名，不是 emoji 字符：机房里的老浏览器缺 emoji
        # 字体会渲染成方块，前端统一按 iconify 渲染成 SVG
        return "noto:red-question-mark" if self._masked(obj) else obj.icon

    def get_metric(self, obj):
        return None if self._masked(obj) else obj.metric

    def get_operator(self, obj):
        return None if self._masked(obj) else obj.operator

    def get_threshold(self, obj):
        return None if self._masked(obj) else obj.threshold

    def get_unlock_rate(self, obj):
        total = get_active_user_count()
        if not total:
            return 0.0
        return round(obj.unlock_count / total * 100, 1)


class PendingAchievementSerializer(serializers.ModelSerializer):
    """待弹窗的解锁记录，无需打码（已解锁）。"""

    id = serializers.IntegerField(source="achievement_id", read_only=True)
    name = serializers.CharField(source="achievement.name", read_only=True)
    description = serializers.CharField(source="achievement.description", read_only=True)
    icon = serializers.CharField(source="achievement.icon", read_only=True)
    rarity = serializers.CharField(source="achievement.rarity", read_only=True)

    class Meta:
        model = UserAchievement
        fields = ("id", "name", "description", "icon", "rarity")
