import json

from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.db.models import Max
from django.http import Http404, JsonResponse
from django.shortcuts import get_object_or_404, render
from django.utils import timezone
from django.views.decorators.http import require_POST


from . import engine
from .models import Game, Play


def _leaders(game, limit=10):
    start = timezone.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
    rows = (Play.objects.filter(game=game, finished=True, user__isnull=False, created_at__gte=start)
            .values('user', 'user__first_name', 'user__email').annotate(best=Max('score')).order_by('-best')[:limit])
    out = []
    for r in rows:
        name = r['user__first_name'] or (r['user__email'] or 'Player').split('@')[0]
        out.append({'name': name[:18], 'score': r['best']})
    return out


def games_index(request):
    games = list(Game.objects.filter(is_active=True))
    for g in games:
        g.leaders = _leaders(g, 3)
    my_coupons = []
    if request.user.is_authenticated:
        my_coupons = [c for c in request.user.coupons.filter(source='game', is_active=True) if c.is_valid()][:5]
    return render(request, 'games/index.html', {'games': games, 'my_coupons': my_coupons})


def game_detail(request, slug):
    game = get_object_or_404(Game, slug=slug, is_active=True)
    others = Game.objects.filter(is_active=True).exclude(pk=game.pk)
    return render(request, 'games/detail.html', {
        'game': game, 'leaders': _leaders(game), 'others': others,
        'card_count': game.cards.filter(is_active=True).count(),
    })


def _limited(request):
    key = f"game-start:{request.META.get('REMOTE_ADDR')}"
    n = cache.get(key, 0)
    if n >= 60:
        return True
    cache.set(key, n + 1, 3600)
    return False


def _own_play(request, token):
    play = Play.objects.select_related('game').filter(token=token).first()
    if play is None:
        raise Http404
    if play.user_id:
        if not request.user.is_authenticated or request.user.pk != play.user_id:
            raise Http404
    elif play.session_key != (request.session.session_key or ''):
        raise Http404
    return play


@require_POST
def play_start(request, slug):
    game = get_object_or_404(Game, slug=slug, is_active=True)
    if _limited(request):
        return JsonResponse({'error': 'অনেকবার খেলা হয়েছে — একটু পরে আবার চেষ্টা করুন।'}, status=429)
    if not request.session.session_key:
        request.session.save()
    play = engine.start(game, request.user, request.session.session_key)
    if play is None:
        return JsonResponse({'error': 'এই খেলার কার্ড এখনো যোগ করা হয়নি।'}, status=400)
    if not play.user_id:
        # Remember guest plays in the session (survives sign-in) so the reward can be claimed after logging in.
        request.session['game_plays'] = (request.session.get('game_plays', []) + [play.token])[-10:]
    return JsonResponse({'token': play.token, 'question': engine.question(play)})


@require_POST
def play_move(request, token):
    play = _own_play(request, token)
    try:
        data = json.loads(request.body or b'{}')
    except ValueError:
        data = {}
    out = engine.answer(play, data)
    return JsonResponse(out, status=400 if out.get('error') and not out.get('finished') else 200)


@require_POST
@login_required
def play_claim(request, token):
    """Signed in after playing as a guest: attach the play to the account and issue the reward."""
    play = Play.objects.select_related('game').filter(token=token, finished=True).first()
    if play is None or (play.user_id and play.user_id != request.user.pk):
        raise Http404
    if not play.user_id:
        if play.token not in request.session.get('game_plays', []):
            raise Http404
        if timezone.now() - play.finished_at > timezone.timedelta(hours=24):
            return JsonResponse({'error': 'এই খেলার পুরস্কার নেওয়ার সময় শেষ।'}, status=400)
        play.user = request.user
        play.save(update_fields=['user'])
    return JsonResponse(engine.finish_summary(play))
