from collections import OrderedDict, defaultdict

from django.db.models import Count, Q
from django.db.models.functions import Extract
from django.utils import timezone
from scrobbles.models import Scrobble


def compute_peak_hours(user, period="all_time"):
    """Group scrobbles by hour of day (0-23) and count them.

    Returns dict: {"hours": [{"hour": N, "count": N}, ...]} sorted by hour.
    """
    hours_qs = (
        Scrobble.objects.filter(user=user, timestamp__isnull=False)
        .annotate(hour=Extract("timestamp", "hour"))
        .values("hour")
        .annotate(count=Count("id"))
        .order_by("hour")
    )

    hours = []
    raw = {row["hour"]: row["count"] for row in hours_qs}
    for h in range(24):
        hours.append({"hour": h, "count": raw.get(h, 0)})

    return {"hours": hours}


def compute_weekly_rhythm(user, period="all_time"):
    """Group scrobble counts by day of the week.

    Uses iso_week_day (1=Monday, 7=Sunday). Returns dict sorted by day index
    with human-readable day names.
    """
    DAY_NAMES = OrderedDict(
        [
            (1, "Monday"),
            (2, "Tuesday"),
            (3, "Wednesday"),
            (4, "Thursday"),
            (5, "Friday"),
            (6, "Saturday"),
            (7, "Sunday"),
        ]
    )

    days_qs = (
        Scrobble.objects.filter(user=user, timestamp__isnull=False)
        .annotate(day=Extract("timestamp", "iso_week_day"))
        .values("day")
        .annotate(count=Count("id"))
        .order_by("day")
    )

    raw = {row["day"]: row["count"] for row in days_qs}
    days = []
    for idx, name in DAY_NAMES.items():
        days.append(
            {
                "day_index": idx,
                "day_name": name,
                "count": raw.get(idx, 0),
            }
        )

    return {"days": days}


def compute_activity_distribution(user, period="all_time"):
    """Proportion of total scrobbles per media type.

    Returns dict: {"distribution": [{"media_type": "...", "count": N,
    "completed": N, "pct": float}, ...]} sorted by count desc, plus
    "total_count".
    """
    from trends.utils import get_date_range

    start, end = get_date_range(period)
    filters = Q(user=user)
    if start:
        filters &= Q(timestamp__gte=start)
    if end:
        filters &= Q(timestamp__lte=end)

    dist_qs = (
        Scrobble.objects.filter(filters)
        .values("media_type")
        .annotate(
            count=Count("id"),
            completed=Count("id", filter=Q(played_to_completion=True)),
        )
        .order_by("-count")
    )

    rows = list(dist_qs)
    total = sum(r["count"] for r in rows) or 1

    distribution = []
    for row in rows:
        distribution.append(
            {
                "media_type": row["media_type"],
                "count": row["count"],
                "completed": row["completed"],
                "pct": round((row["count"] / total) * 100, 1),
            }
        )

    return {
        "distribution": distribution,
        "total_count": sum(r["count"] for r in rows),
    }
