"""
Badge Engine — Evaluates badge conditions and awards badges to users.

Called after every XP award (via xp_engine.award_xp_for_completion).
Checks all un-earned badges against the user's current state and awards
any that are now satisfied.

Badge conditions are stored as JSONB in the badges table.
Supported condition_types:
  - xp_threshold:          {"threshold": 500}
  - activities_completed:   {"count": 5}
  - category_count:         {"category": "course", "count": 3}
  - category_complete_all:  {"category": "reading"}
  - hackathon_count:        {"count": 1}
  - platform_count:         {"platform": "udemy", "count": 3}
  - department_first:       {}  (first in department to reach a milestone)
"""

from uuid import UUID

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.activity import Activity, ActivityCompletion
from app.models.gamification import ActivityFeed, Badge, UserBadge
from app.models.user import User


async def evaluate_badges(db: AsyncSession, user_id: UUID) -> list[dict]:
    """
    Check all active badges the user hasn't earned yet.
    Award any that are now satisfied. Return badge_earned events.
    """
    events: list[dict] = []

    # Get user
    user = await db.get(User, user_id)
    if not user:
        return events

    # Get badges user already has
    earned_result = await db.execute(
        select(UserBadge.badge_id).where(UserBadge.user_id == user_id)
    )
    earned_badge_ids = {row[0] for row in earned_result.all()}

    # Get all active badges not yet earned
    badges_result = await db.execute(
        select(Badge).where(Badge.is_active == True, ~Badge.id.in_(earned_badge_ids))  # noqa: E712
    )
    unevaluated_badges = badges_result.scalars().all()

    for badge in unevaluated_badges:
        satisfied = await _check_condition(db, user_id, user, badge)
        if satisfied:
            # Award the badge
            db.add(UserBadge(user_id=user_id, badge_id=badge.id))

            # Log to activity feed
            db.add(ActivityFeed(
                user_id=user_id,
                event_type="badge_earned",
                event_data={
                    "badge_id": str(badge.id),
                    "badge_name": badge.name,
                    "badge_icon": badge.icon,
                },
            ))

            events.append({
                "type": "badge_earned",
                "user_id": str(user_id),
                "data": {
                    "badge_name": badge.name,
                    "badge_icon": badge.icon,
                    "badge_description": badge.description,
                },
            })

    return events


async def _check_condition(
    db: AsyncSession, user_id: UUID, user: User, badge: Badge
) -> bool:
    """Evaluate a single badge condition against user's current state."""
    ctype = badge.condition_type
    cval = badge.condition_value or {}

    if ctype == "xp_threshold":
        return user.total_xp >= cval.get("threshold", 0)

    elif ctype == "activities_completed":
        count = await _completed_count(db, user_id)
        return count >= cval.get("count", 0)

    elif ctype == "category_count":
        count = await _completed_count(db, user_id, category=cval.get("category"))
        return count >= cval.get("count", 0)

    elif ctype == "category_complete_all":
        category = cval.get("category")
        if not category:
            return False
        # Count total active activities in this category
        total_result = await db.execute(
            select(func.count(Activity.id)).where(
                Activity.category == category, Activity.is_active == True  # noqa: E712
            )
        )
        total = total_result.scalar() or 0
        completed = await _completed_count(db, user_id, category=category)
        return total > 0 and completed >= total

    elif ctype == "hackathon_count":
        count = await _completed_count(db, user_id, category="hackathon")
        return count >= cval.get("count", 0)

    elif ctype == "platform_count":
        count = await _completed_count(db, user_id, platform=cval.get("platform"))
        return count >= cval.get("count", 0)

    elif ctype == "department_first":
        # TODO: Implement - check if user is first in their department
        # to complete a certain number of activities or reach an XP threshold
        return False

    return False


async def _completed_count(
    db: AsyncSession,
    user_id: UUID,
    category: str | None = None,
    platform: str | None = None,
) -> int:
    """Count completed activities for a user, optionally filtered by category or platform."""
    query = (
        select(func.count(ActivityCompletion.id))
        .where(
            ActivityCompletion.user_id == user_id,
            ActivityCompletion.status == "completed",
        )
    )

    if category or platform:
        query = query.join(Activity, ActivityCompletion.activity_id == Activity.id)
        if category:
            query = query.where(Activity.category == category)
        if platform:
            query = query.where(Activity.platform == platform)

    result = await db.execute(query)
    return result.scalar() or 0
