"""Tier 1 analytics — aggregates Application rows into dashboard-ready structures.

Everything returns plain dicts/lists so the view can pass them straight to the
template as JSON for Chart.js.
"""
from collections import Counter, OrderedDict
from datetime import datetime, timezone, timedelta

from models import (Application, GENDERS, EDUCATION_LEVELS, EMPLOYMENT_STATUSES,
                    LANGUAGES, LEARNING_DURATIONS, CHALLENGES, REASONS, HEARD_FROM, TRACKS)


def _counts(values, order=None, top=None):
    """Return {label: count}. If `order` given, keep that order (0-filled).
    Else sort by count desc; `top` caps the number of bars."""
    c = Counter(v for v in values if v not in (None, ''))
    if order:
        return OrderedDict((label, c.get(label, 0)) for label in order)
    items = c.most_common(top) if top else c.most_common()
    return OrderedDict(items)


def _age_bucket(age):
    if age is None:
        return None
    if age < 18:
        return 'Under 18'
    if age <= 24:
        return '18–24'
    if age <= 34:
        return '25–34'
    if age <= 44:
        return '35–44'
    return '45+'


def _hours_bucket(h):
    if h is None:
        return None
    if h <= 5:
        return '0–5'
    if h <= 10:
        return '6–10'
    if h <= 20:
        return '11–20'
    if h <= 30:
        return '21–30'
    return '30+'


def build_analytics(apps):
    """apps: list[Application]. Returns a dict consumed by the dashboard template."""
    now = datetime.now(timezone.utc)
    today = now.date()
    week_ago = now - timedelta(days=7)

    def _dt(a):
        d = a.created_at
        if d is None:
            return None
        if d.tzinfo is None:
            d = d.replace(tzinfo=timezone.utc)
        return d

    total = len(apps)
    completed = sum(1 for a in apps if a.status == 'complete')
    incomplete = total - completed
    today_count = sum(1 for a in apps if _dt(a) and _dt(a).date() == today)
    week_count = sum(1 for a in apps if _dt(a) and _dt(a) >= week_ago)

    # multi-selects
    all_langs = [lang for a in apps for lang in a.languages_list()]
    all_challenges = [ch for a in apps for ch in a.challenges_list()]

    kpis = {
        'total': total,
        'completed': completed,
        'incomplete': incomplete,
        'today': today_count,
        'this_week': week_count,
    }

    return {
        'kpis': kpis,
        # demographics
        'gender': _counts((a.gender for a in apps), order=GENDERS),
        'age': _counts((_age_bucket(a.age) for a in apps),
                       order=['Under 18', '18–24', '25–34', '35–44', '45+']),
        'country': _counts((a.country for a in apps), top=10),
        'city': _counts((a.city for a in apps), top=10),
        'education': _counts((a.education for a in apps), order=EDUCATION_LEVELS),
        'employment': _counts((a.employment_status for a in apps), order=EMPLOYMENT_STATUSES),
        # technology
        'programming_experience': OrderedDict([
            ('Yes', sum(1 for a in apps if a.has_programming_experience)),
            ('No', sum(1 for a in apps if not a.has_programming_experience)),
        ]),
        'languages': _counts(all_langs, order=LANGUAGES),
        'learning_duration': _counts((a.learning_duration for a in apps), order=LEARNING_DURATIONS),
        # experience
        'experience': OrderedDict([
            ('Has projects', sum(1 for a in apps if a.has_projects)),
            ('No projects', sum(1 for a in apps if not a.has_projects)),
            ('Internship exp.', sum(1 for a in apps if a.internship_experience)),
            ('Team project exp.', sum(1 for a in apps if a.team_project_experience)),
        ]),
        # challenges (ranked)
        'challenges': _counts(all_challenges, order=CHALLENGES),
        # goals
        'reasons': _counts((a.reason_for_joining for a in apps), order=REASONS),
        'weekly_hours': _counts((_hours_bucket(a.weekly_hours) for a in apps),
                                order=['0–5', '6–10', '11–20', '21–30', '30+']),
        'commitment': _counts((a.commitment_score for a in apps),
                              order=list(range(1, 11))),
        # marketing
        'heard_from': _counts((a.heard_from for a in apps), order=HEARD_FROM),
        # track (handy extra)
        'track': _counts((a.track for a in apps), order=TRACKS),
    }
