# retoor <retoor@molodetz.nl>

from devplacepy.database import (
    get_table,
    get_user_stars,
    record_activity,
    record_unique_activity,
)
from devplacepy.services.background import background
from devplacepy.utils.authcache import clear_user_cache
from devplacepy.utils.badges import award_badge, notify_badge, LEVEL_BADGES
from devplacepy.utils.notifications import create_notification

LEVEL_XP = 100

XP_POST = 10
XP_COMMENT = 2
XP_PROJECT = 15
XP_GIST = 5
XP_UPVOTE = 5
XP_FOLLOW = 5
XP_QUIZ = 8
XP_QUIZ_PUBLISH = 12
XP_QUIZ_COMPLETE = 4
XP_BATTLE_PART = 5
XP_BATTLE_WIN = 15
XP_BATTLE_TOP = 10


def level_for_xp(xp: int) -> int:
    return 1 + max(0, xp) // LEVEL_XP


def award_xp(user_uid: str, amount: int) -> dict:
    users = get_table("users")
    user = users.find_one(uid=user_uid)
    if not user or amount <= 0:
        return {
            "xp": user.get("xp", 0) if user else 0,
            "level": user.get("level", 1) if user else 1,
            "leveled_up": False,
        }
    current_xp = user.get("xp", 0) or 0
    current_level = user.get("level", 1) or 1
    new_xp = max(0, current_xp + amount)
    new_level = level_for_xp(new_xp)
    users.update({"uid": user_uid, "xp": new_xp, "level": new_level}, ["uid"])
    clear_user_cache(user_uid, propagate=False)
    leveled_up = new_level > current_level
    from devplacepy.services.audit import record as audit

    audit.record_system(
        "reward.xp.grant",
        actor_kind="user",
        actor_uid=user_uid,
        new_value=amount,
        metadata={"amount": amount, "total_xp": new_xp},
        summary=f"user earned {amount} XP",
        links=[audit.actor(user_uid)],
    )
    if leveled_up:
        audit.record_system(
            "reward.level.up",
            actor_kind="user",
            actor_uid=user_uid,
            old_value=current_level,
            new_value=new_level,
            metadata={"level": new_level},
            summary=f"user reached level {new_level}",
            links=[audit.actor(user_uid)],
        )
        create_notification(
            user_uid,
            "level",
            f"You reached level {new_level}",
            user_uid,
            f"/profile/{user['username']}",
        )
        for level in range(current_level + 1, new_level + 1):
            badge_name = LEVEL_BADGES.get(level)
            if badge_name and award_badge(user_uid, badge_name):
                notify_badge(user_uid, badge_name)
    return {"xp": new_xp, "level": new_level, "leveled_up": leveled_up}


_COUNT_MILESTONES = [
    ("Prolific", "posts", 10),
    ("Wordsmith", "posts", 50),
    ("Veteran", "posts", 100),
    ("Conversationalist", "comments", 25),
    ("Debater", "comments", 100),
    ("Builder", "projects", 5),
    ("Architect", "projects", 10),
    ("Snippet Collector", "gists", 5),
    ("Rising Star", "stars", 25),
    ("Star Author", "stars", 100),
    ("Superstar", "stars", 500),
    ("Popular", "followers", 10),
    ("Influencer", "followers", 50),
    ("Celebrity", "followers", 100),
    ("Connector", "following", 10),
    ("On Fire", "streak", 7),
    ("Dedicated", "streak", 30),
    ("Unstoppable", "streak", 100),
]


def _milestone_metrics(user_uid: str):
    cache: dict[str, int] = {}

    def resolve(key: str) -> int:
        if key in cache:
            return cache[key]
        if key == "posts":
            value = get_table("posts").count(user_uid=user_uid, deleted_at=None)
        elif key == "comments":
            value = get_table("comments").count(user_uid=user_uid, deleted_at=None)
        elif key == "projects":
            value = get_table("projects").count(user_uid=user_uid, deleted_at=None)
        elif key == "gists":
            value = get_table("gists").count(user_uid=user_uid, deleted_at=None)
        elif key == "stars":
            value = get_user_stars(user_uid)
        elif key == "followers":
            value = get_table("follows").count(following_uid=user_uid, deleted_at=None)
        elif key == "following":
            value = get_table("follows").count(follower_uid=user_uid, deleted_at=None)
        elif key == "streak":
            from devplacepy.database import get_streaks

            value = get_streaks(user_uid)["current"]
        else:
            value = 0
        cache[key] = value
        return value

    return resolve


def check_milestone_badges(user_uid: str) -> list:
    held = {row["badge_name"] for row in get_table("badges").find(user_uid=user_uid)}
    pending = [m for m in _COUNT_MILESTONES if m[0] not in held]
    if not pending:
        return []
    metric = _milestone_metrics(user_uid)
    awarded = []
    for badge_name, metric_key, threshold in pending:
        if metric(metric_key) >= threshold and award_badge(user_uid, badge_name):
            awarded.append(badge_name)
            if metric_key == "streak":
                from devplacepy.services.audit import record as audit

                audit.record_system(
                    "reward.streak.milestone",
                    actor_kind="user",
                    actor_uid=user_uid,
                    metadata={"streak": metric("streak")},
                    summary=f"user reached a {metric('streak')}-day streak",
                    links=[audit.actor(user_uid)],
                )
    for badge_name in awarded:
        notify_badge(user_uid, badge_name)
    return awarded


def award_rewards(user_uid: str, amount: int, first_badge: str | None = None) -> None:
    background.submit(_apply_rewards, user_uid, amount, first_badge)


def _apply_rewards(user_uid: str, amount: int, first_badge: str | None = None) -> None:
    if first_badge:
        award_badge(user_uid, first_badge)
    award_xp(user_uid, amount)
    check_milestone_badges(user_uid)


ACHIEVEMENTS = {
    "docs.read": [(1, "Curious"), (5, "Studious"), (15, "Scholar")],
    "devii": [(1, "AI Curious"), (25, "AI Whisperer")],
    "fork": [(1, "First Fork"), (5, "Forker")],
    "battle_create": [(1, "Instigator")],
    "battle_fight": [(1, "First Blood"), (50, "War Veteran")],
    "battle_win": [(1, "Champion")],
    "zip": [(1, "Archivist")],
    "seo": [(1, "SEO Auditor")],
    "isslop": [(1, "Slop Hunter")],
    "deepsearch": [(1, "Researcher"), (10, "Deep Diver")],
    "container": [(1, "Container Captain")],
    "message": [(1, "Messenger"), (100, "Chatterbox")],
    "bookmark": [(1, "Bookmarker"), (25, "Curator")],
    "reaction": [(1, "First Reaction"), (50, "Cheerleader")],
    "vote": [(1, "First Star Given"), (50, "Supporter"), (250, "Patron")],
    "follow": [(1, "Friendly")],
    "upload": [(1, "First Upload")],
    "project_file": [(1, "File Creator")],
    "issue": [(1, "Bug Reporter")],
    "poll": [(1, "Pollster")],
    "profile": [(1, "Profiled")],
    "harvest": [(1, "Green Thumb"), (50, "Master Farmer")],
    "water": [(1, "Good Neighbor")],
    "harvest_stolen": [(1, "Cat Burglar")],
    "got_stolen_from": [(1, "Robbed")],
    "infra_bought": [(1, "Enterprise Ready")],
    "defense_upgraded": [(1, "Fort Knox")],
    "cosmetic_bought": [(1, "Style Points")],
    "underdog_raid": [(1, "David vs Goliath")],
    "quiz_publish": [(1, "Quiz Author"), (10, "Quiz Master")],
    "quiz_complete": [(1, "Quiz Taker"), (25, "Quiz Regular")],
    "quiz_perfect": [(1, "Flawless")],
}

UNIQUE_ACTIONS = {"docs.read"}


def track_action(user_uid: str, action: str, target: str | None = None) -> None:
    if not user_uid or action not in ACHIEVEMENTS:
        return
    background.submit(_apply_achievements, user_uid, action, target)


def _apply_achievements(user_uid: str, action: str, target: str | None) -> None:
    if action in UNIQUE_ACTIONS:
        if target is None:
            return
        count = record_unique_activity(user_uid, action, target)
    else:
        count = record_activity(user_uid, action)
    if not count:
        return
    for threshold, badge_name in ACHIEVEMENTS.get(action, []):
        if count >= threshold and award_badge(user_uid, badge_name):
            notify_badge(user_uid, badge_name)
