"""Template tag that powers the admin analytics dashboard.

Everything is derived live from the data so the figures always match reality
(money flows from :class:`~main.models.Transaction`, the same source
:mod:`main.services` uses for balances).

Two kinds of metric:

* **flow** metrics (registrations, deposits, withdrawals, commission) are scoped
  to the selected time window (``?period=day|week|month|year|all``);
* **state** metrics (total users, active/frozen/banned, online) are point-in-time
  snapshots and ignore the window.
"""
from datetime import timedelta
from decimal import Decimal

from django import template
from django.contrib.auth.models import User
from django.db.models import Count, Sum
from django.utils import timezone

from .. import services
from ..models import (
    Agent, Profile, Transaction, TicketBooking,
    WithdrawalRequest, DepositRequest, SetApprovalRequest,
)

register = template.Library()

# Selectable windows, in display order. ``None`` start == all-time.
PERIODS = [
    ('day',   'Today'),
    ('week',  'This Week'),
    ('month', 'This Month'),
    ('year',  'This Year'),
    ('all',   'All Time'),
]
_PERIOD_KEYS = {key for key, _ in PERIODS}
DEFAULT_PERIOD = 'all'


def _period_start(period, now):
    """Calendar-aligned lower bound for a window, or ``None`` for all-time."""
    local = timezone.localtime(now)
    midnight = local.replace(hour=0, minute=0, second=0, microsecond=0)
    if period == 'day':
        return midnight
    if period == 'week':
        return midnight - timedelta(days=local.weekday())   # Monday this week
    if period == 'month':
        return midnight.replace(day=1)
    if period == 'year':
        return midnight.replace(month=1, day=1)
    return None  # all


def _sum(qs, field='amount'):
    return qs.aggregate(total=Sum(field))['total'] or Decimal('0')


def _tx(kind, start):
    """Transactions of ``kind`` within the window (``start`` may be None)."""
    qs = Transaction.objects.filter(kind=kind)
    return qs.filter(created_at__gte=start) if start else qs


def _grouped_tx_sum(kind, start):
    """``{agent_id: summed amount}`` for ``kind`` transactions in the window,
    grouped by the depositing user's agent."""
    rows = (_tx(kind, start)
            .values('user__profile__agent_id')
            .annotate(total=Sum('amount')))
    return {r['user__profile__agent_id']: r['total'] or Decimal('0') for r in rows}


def _agent_rows(start):
    """Per-agent analytics for the table: downline size, money in/out and the
    commission their users generated in the window."""
    # Total users under each agent (all-time) and those who registered in-window.
    total_by_agent = {r['agent_id']: r['n']
                      for r in Profile.objects.values('agent_id').annotate(n=Count('id'))}
    new_qs = Profile.objects.filter(created_at__gte=start) if start else Profile.objects.all()
    new_by_agent = {r['agent_id']: r['n']
                    for r in new_qs.values('agent_id').annotate(n=Count('id'))}

    deposits = _grouped_tx_sum(Transaction.DEPOSIT, start)
    withdrawals = _grouped_tx_sum(Transaction.WITHDRAWAL, start)   # stored negative
    commission = _grouped_tx_sum(Transaction.COMMISSION, start)

    rows = []
    for agent in Agent.objects.all():
        aid = agent.id
        rows.append({
            'name': agent.name,
            'code': agent.referral_code,
            'uid': agent.uid,
            'is_active': agent.is_active,
            'total_users': total_by_agent.get(aid, 0),
            'new_users': new_by_agent.get(aid, 0),
            'deposits': services.format_eur(deposits.get(aid, Decimal('0'))),
            'deposits_raw': deposits.get(aid, Decimal('0')),
            'withdrawals': services.format_eur(abs(withdrawals.get(aid, Decimal('0')))),
            'commission': services.format_eur(commission.get(aid, Decimal('0'))),
            'commission_raw': commission.get(aid, Decimal('0')),
        })

    # Users not grouped under any agent (registered directly), if any.
    unassigned_total = total_by_agent.get(None, 0)
    if unassigned_total:
        rows.append({
            'name': '— Unassigned —', 'code': '', 'uid': '', 'is_active': True,
            'total_users': unassigned_total,
            'new_users': new_by_agent.get(None, 0),
            'deposits': services.format_eur(deposits.get(None, Decimal('0'))),
            'deposits_raw': deposits.get(None, Decimal('0')),
            'withdrawals': services.format_eur(abs(withdrawals.get(None, Decimal('0')))),
            'commission': services.format_eur(commission.get(None, Decimal('0'))),
            'commission_raw': commission.get(None, Decimal('0')),
            'unassigned': True,
        })

    # Highest commission generators first; unassigned bucket sinks to the bottom.
    rows.sort(key=lambda r: (r.get('unassigned', False), -r['commission_raw']))
    return rows


def _status_donut(active, frozen, banned):
    """Conic-gradient string + legend for the account-status donut chart."""
    segments = [
        ('Active', active, '#10b981'),
        ('Frozen', frozen, '#06b6d4'),
        ('Banned', banned, '#ef4444'),
    ]
    total = active + frozen + banned
    legend, stops, deg = [], [], 0.0
    for label, count, color in segments:
        sweep = (count / total * 360) if total else 0
        if sweep > 0:
            stops.append(f'{color} {deg:.2f}deg {deg + sweep:.2f}deg')
            deg += sweep
        legend.append({
            'label': label, 'count': count, 'color': color,
            'pct': round(count / total * 100) if total else 0,
        })
    gradient = 'conic-gradient(%s)' % ', '.join(stops) if stops else 'conic-gradient(#e2e8f0 0deg 360deg)'
    return {'gradient': gradient, 'legend': legend, 'total': total}


def _deposit_bars(agent_rows, limit=6):
    """Top agents by deposits as proportional bars for the bar chart."""
    real = [r for r in agent_rows if not r.get('unassigned')]
    real.sort(key=lambda r: r['deposits_raw'], reverse=True)
    top = real[:limit]
    biggest = max((r['deposits_raw'] for r in top), default=Decimal('0'))
    bars = []
    for r in top:
        value = r['deposits_raw']
        pct = int(value / biggest * 100) if biggest else 0
        bars.append({
            'name': r['name'], 'value': r['deposits'],
            'pct': max(pct, 3) if value > 0 else 0,
        })
    return bars


@register.simple_tag(takes_context=True)
def admin_dashboard_stats(context):
    """Return the period selector, headline cards and per-agent table.

    Used as ``{% admin_dashboard_stats as stats %}`` in dashboard_index.html.
    """
    request = context.get('request')
    period = (request.GET.get('period') if request else None) or DEFAULT_PERIOD
    if period not in _PERIOD_KEYS:
        period = DEFAULT_PERIOD

    now = timezone.now()
    start = _period_start(period, now)
    period_label = dict(PERIODS)[period]
    in_window = 'all time' if start is None else period_label.lower()

    # ---- flow metrics (respect the window) ----
    new_users = (User.objects.filter(date_joined__gte=start) if start else User.objects).count()
    new_agents = (Agent.objects.filter(created_at__gte=start) if start else Agent.objects).count()
    deposits_total = _sum(_tx(Transaction.DEPOSIT, start))
    withdrawals_total = abs(_sum(_tx(Transaction.WITHDRAWAL, start)))
    commission_total = _sum(_tx(Transaction.COMMISSION, start))

    # ---- state metrics (point-in-time) ----
    online_cutoff = now - timedelta(minutes=5)
    online_users = Profile.objects.filter(last_seen__gte=online_cutoff).count()
    total_users = User.objects.count()
    active = Profile.objects.filter(status=Profile.ACTIVE).count()
    frozen = Profile.objects.filter(status=Profile.FROZEN).count()
    banned = Profile.objects.filter(status=Profile.BANNED).count()

    # pending work (always current)
    pending_deposits = DepositRequest.objects.filter(status=DepositRequest.PENDING).count()
    pending_withdrawals = WithdrawalRequest.objects.filter(status=WithdrawalRequest.PENDING).count()
    pending_approvals = SetApprovalRequest.objects.filter(status=SetApprovalRequest.PENDING).count()
    completed_tickets = TicketBooking.objects.filter(status=TicketBooking.COMPLETED).count()

    cards = [
        {'label': 'Users Registered', 'value': new_users, 'icon': '👥', 'tone': 'blue',
         'sub': f'new · {in_window}', 'scope': 'period', 'action': '/admin/auth/user/'},
        {'label': 'Agents Registered', 'value': new_agents, 'icon': '🛡️', 'tone': 'indigo',
         'sub': f'new · {in_window}', 'scope': 'period', 'action': '/admin/main/agent/'},
        {'label': 'Total Deposits', 'value': services.format_eur(deposits_total), 'icon': '💰',
         'tone': 'green', 'sub': f'from clients · {in_window}', 'scope': 'period',
         'action': '/admin/main/transaction/?kind__exact=deposit'},
        {'label': 'Total Withdrawals', 'value': services.format_eur(withdrawals_total), 'icon': '🏧',
         'tone': 'amber', 'sub': f'paid out · {in_window}', 'scope': 'period',
         'action': '/admin/main/transaction/?kind__exact=withdrawal'},
        {'label': 'Commission Paid', 'value': services.format_eur(commission_total), 'icon': '📈',
         'tone': 'cyan', 'sub': f'to clients · {in_window}', 'scope': 'period',
         'action': '/admin/main/transaction/?kind__exact=commission'},
        {'label': 'Active Accounts', 'value': active, 'icon': '✅', 'tone': 'green',
         'sub': 'unfrozen · current', 'scope': 'current', 'action': '/admin/auth/user/?is_active__exact=1'},
        {'label': 'Frozen Accounts', 'value': frozen, 'icon': '❄️', 'tone': 'cyan',
         'sub': 'temporary block · current', 'scope': 'current',
         'action': '/admin/main/profile/?status__exact=frozen', 'attention': frozen > 0},
        {'label': 'Banned Accounts', 'value': banned, 'icon': '🚫', 'tone': 'red',
         'sub': 'permanent block · current', 'scope': 'current',
         'action': '/admin/main/profile/?status__exact=banned', 'attention': banned > 0},
        {'label': 'Total Users', 'value': total_users, 'icon': '🧑‍🤝‍🧑', 'tone': 'blue',
         'sub': f'{online_users} online now · current', 'scope': 'current', 'action': '/admin/auth/user/'},
        {'label': 'Tickets Completed', 'value': completed_tickets, 'icon': '🎫', 'tone': 'indigo',
         'sub': 'all-time · current', 'scope': 'current', 'action': '/admin/main/ticketbooking/'},
    ]

    # pending-work strip (compact, always shown)
    pending = [
        {'label': 'Deposits to review', 'value': pending_deposits,
         'action': '/admin/main/depositrequest/?status__exact=pending', 'hot': pending_deposits > 0},
        {'label': 'Withdrawals to pay', 'value': pending_withdrawals,
         'action': '/admin/main/withdrawalrequest/?status__exact=pending', 'hot': pending_withdrawals > 0},
        {'label': 'Set approvals', 'value': pending_approvals,
         'action': '/admin/main/setapprovalrequest/?status__exact=pending', 'hot': pending_approvals > 0},
    ]

    periods = [{'key': k, 'label': lbl, 'active': k == period, 'url': f'?period={k}'}
               for k, lbl in PERIODS]

    agent_rows = _agent_rows(start)

    return {
        'period': period,
        'period_label': period_label,
        'periods': periods,
        'cards': cards,
        'pending': pending,
        'agent_rows': agent_rows,
        'charts': {
            'status': _status_donut(active, frozen, banned),
            'deposit_bars': _deposit_bars(agent_rows),
        },
        'generated_at': now,
    }
