# ==============================================
# Breakpilot Drive - Extended DB Methods
# ==============================================
# Leaderboard extensions, parent dashboard, achievements, progress.

import logging
from typing import List, Dict, Any

from .database_models import Achievement, ACHIEVEMENTS

logger = logging.getLogger(__name__)


class ExtrasMixin:
    """Mixin providing leaderboard, parent dashboard, and achievement methods."""

    async def get_class_leaderboard(
        self,
        class_id: str,
        timeframe: str = "week",
        limit: int = 10
    ) -> List[Dict[str, Any]]:
        """
        Get leaderboard filtered by class.

        Note: Requires class_id to be stored in user metadata or
        a separate class_memberships table. For now, this is a
        placeholder that can be extended.
        """
        # For now, fall back to regular leaderboard
        # TODO: Join with class_memberships table when available
        return await self.get_leaderboard(timeframe, limit)

    async def get_leaderboard_with_names(
        self,
        timeframe: str = "day",
        limit: int = 10,
        anonymize: bool = True
    ) -> List[Dict[str, Any]]:
        """Get leaderboard with anonymized display names."""
        leaderboard = await self.get_leaderboard(timeframe, limit)

        # Anonymize names for privacy (e.g., "Spieler 1", "Spieler 2")
        if anonymize:
            for entry in leaderboard:
                entry["display_name"] = f"Spieler {entry['rank']}"
        else:
            # In production: Join with users table to get real names
            for entry in leaderboard:
                entry["display_name"] = f"Spieler {entry['rank']}"

        return leaderboard

    # ==============================================
    # Parent Dashboard Methods
    # ==============================================

    async def get_children_stats(
        self,
        children_ids: List[str]
    ) -> List[Dict[str, Any]]:
        """Get stats for multiple children (parent dashboard)."""
        if not children_ids:
            return []

        results = []
        for child_id in children_ids:
            state = await self.get_learning_state(child_id)
            sessions = await self.get_user_sessions(child_id, limit=5)

            results.append({
                "student_id": child_id,
                "learning_state": state.to_dict() if state else None,
                "recent_sessions": sessions,
                "has_played": state is not None and state.total_sessions > 0,
            })

        return results

    async def get_progress_over_time(
        self,
        student_id: str,
        days: int = 30
    ) -> List[Dict[str, Any]]:
        """Get learning progress over time for charts."""
        await self._ensure_connected()
        if not self._pool:
            return []

        try:
            async with self._pool.acquire() as conn:
                rows = await conn.fetch(
                    """
                    SELECT
                        DATE(ended_at) as date,
                        COUNT(*) as sessions,
                        SUM(score) as total_score,
                        SUM(questions_answered) as questions,
                        SUM(questions_correct) as correct,
                        AVG(difficulty_level) as avg_difficulty
                    FROM game_sessions
                    WHERE student_id = $1
                      AND ended_at > NOW() - make_interval(days => $2)
                    GROUP BY DATE(ended_at)
                    ORDER BY date ASC
                    """,
                    student_id, days
                )

                return [
                    {
                        "date": row["date"].isoformat(),
                        "sessions": row["sessions"],
                        "total_score": int(row["total_score"]),
                        "questions": row["questions"],
                        "correct": row["correct"],
                        "accuracy": row["correct"] / row["questions"] if row["questions"] > 0 else 0,
                        "avg_difficulty": float(row["avg_difficulty"]) if row["avg_difficulty"] else 3.0,
                    }
                    for row in rows
                ]
        except Exception as e:
            logger.error(f"Failed to get progress over time: {e}")

        return []

    # ==============================================
    # Achievement Methods
    # ==============================================

    async def get_student_achievements(
        self,
        student_id: str
    ) -> List[Achievement]:
        """Get achievements with unlock status for a student."""
        await self._ensure_connected()

        # Get student stats for progress calculation
        state = await self.get_learning_state(student_id)

        # Calculate progress for each achievement
        achievements = []
        for a in ACHIEVEMENTS:
            achievement = Achievement(
                id=a.id,
                name=a.name,
                description=a.description,
                icon=a.icon,
                category=a.category,
                threshold=a.threshold,
            )

            # Calculate progress based on category
            if state:
                if a.category == "general":
                    achievement.progress = state.total_sessions
                    achievement.unlocked = state.total_sessions >= a.threshold
                elif a.category == "time":
                    achievement.progress = state.total_play_time_minutes
                    achievement.unlocked = state.total_play_time_minutes >= a.threshold
                elif a.category == "level":
                    achievement.progress = state.overall_level
                    achievement.unlocked = state.overall_level >= a.threshold
                elif a.category == "accuracy":
                    if a.id == "accuracy_80" and state.questions_answered >= 50:
                        achievement.progress = int(state.accuracy * 100)
                        achievement.unlocked = state.accuracy >= 0.8

            achievements.append(achievement)

        # Check DB for unlocked achievements (streak, score, perfect game)
        if self._pool:
            try:
                async with self._pool.acquire() as conn:
                    # Check for score achievements
                    max_score = await conn.fetchval(
                        "SELECT MAX(score) FROM game_sessions WHERE student_id = $1",
                        student_id
                    )
                    if max_score:
                        for a in achievements:
                            if a.category == "score":
                                a.progress = max_score
                                a.unlocked = max_score >= a.threshold

                    # Check for perfect game
                    perfect = await conn.fetchval(
                        """
                        SELECT COUNT(*) FROM game_sessions
                        WHERE student_id = $1
                          AND questions_answered >= 5
                          AND questions_correct = questions_answered
                        """,
                        student_id
                    )
                    for a in achievements:
                        if a.id == "perfect_game":
                            a.progress = 100 if perfect and perfect > 0 else 0
                            a.unlocked = perfect is not None and perfect > 0
            except Exception as e:
                logger.error(f"Failed to check achievements: {e}")

        return achievements

    async def check_new_achievements(
        self,
        student_id: str,
        session_score: int,
        session_accuracy: float,
        streak: int
    ) -> List[Achievement]:
        """
        Check for newly unlocked achievements after a session.
        Returns list of newly unlocked achievements.
        """
        all_achievements = await self.get_student_achievements(student_id)
        newly_unlocked = []

        for a in all_achievements:
            # Check streak achievements
            if a.category == "streak" and streak >= a.threshold and not a.unlocked:
                a.unlocked = True
                newly_unlocked.append(a)

            # Check score achievements
            if a.category == "score" and session_score >= a.threshold and not a.unlocked:
                a.unlocked = True
                newly_unlocked.append(a)

            # Check perfect game
            if a.id == "perfect_game" and session_accuracy == 1.0:
                if not a.unlocked:
                    a.unlocked = True
                    newly_unlocked.append(a)

        return newly_unlocked
