import random
import math
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.parsers import JSONParser, FormParser
from django.shortcuts import get_object_or_404
from .models import KandegeRound, KandegeBet


def generate_crash_point():
    r = random.random()
    if r < 0.05:
        return round(random.uniform(1.00, 1.10), 2)
    crash = 0.99 / (1 - r)
    return round(min(crash, 200.0), 2)


def get_current_multiplier(started_at):
    elapsed = min((timezone.now() - started_at).total_seconds(), 100)
    return round(math.exp(0.1 * elapsed), 2)


def serialize_round(round_obj, user=None):
    now = timezone.now()
    bets = []
    for b in round_obj.bets.select_related("user").all():
        bets.append({
            "user_id": b.user.id,
            "username": b.user.username,
            "amount": b.amount,
            "cashed_out": b.cashed_out,
            "cashout_at": b.cashout_at,
            "winnings": b.winnings,
            "auto_cashout": b.auto_cashout,
        })
    my_bet = None
    if user:
        ub = round_obj.bets.filter(user=user).first()
        if ub:
            my_bet = {
                "amount": ub.amount,
                "cashed_out": ub.cashed_out,
                "cashout_at": ub.cashout_at,
                "winnings": ub.winnings,
                "auto_cashout": ub.auto_cashout,
            }
    time_left = 0
    if round_obj.status == "waiting":
        age = (now - round_obj.created_at).total_seconds()
        time_left = max(0, int(7 - age))

    return {
        "id": round_obj.id,
        "status": round_obj.status,
        "multiplier": round_obj.multiplier,
        "crash_point": round_obj.crash_point if round_obj.status == "crashed" else None,
        "started_at": round_obj.started_at.isoformat() if round_obj.started_at else None,
        "created_at": round_obj.created_at.isoformat(),
        "time_left": time_left,
        "bets": bets,
        "my_bet": my_bet,
    }


def get_or_create_round():
    # Prefer waiting round first (for betting)
    r = KandegeRound.objects.filter(status="waiting").order_by("-created_at").first()
    if r:
        return r
    # Then flying
    r = KandegeRound.objects.filter(status="flying").order_by("-created_at").first()
    if r:
        return r
    # Create new
    r = KandegeRound.objects.create(
        crash_point=generate_crash_point(),
        status="waiting",
    )
    return r


def advance_round(r):
    now = timezone.now()
    if r.status == "waiting":
        age = (now - r.created_at).total_seconds()
        if age >= 7:
            r.status = "flying"
            r.started_at = now
            r.multiplier = 1.00
            r.save(update_fields=["status", "started_at", "multiplier"])

    if r.status == "flying":
        m = get_current_multiplier(r.started_at)
        # Process auto cashouts
        for bet in r.bets.filter(cashed_out=False, auto_cashout__isnull=False):
            if m >= bet.auto_cashout:
                process_cashout(bet, bet.auto_cashout)
        if m >= r.crash_point:
            r.status = "crashed"
            r.multiplier = r.crash_point
            r.crashed_at = now
            r.save(update_fields=["status", "multiplier", "crashed_at"])
            # New round
            KandegeRound.objects.create(
                crash_point=generate_crash_point(),
                status="waiting",
            )
        else:
            r.multiplier = m
            r.save(update_fields=["multiplier"])


def process_cashout(bet, multiplier):
    winnings = int(bet.amount * multiplier)
    bet.cashed_out = True
    bet.cashout_at = multiplier
    bet.winnings = winnings
    bet.save(update_fields=["cashed_out", "cashout_at", "winnings"])
    from wallet.models import Wallet, Transaction
    w, _ = Wallet.objects.get_or_create(user=bet.user)
    w.balance += winnings
    w.save(update_fields=["balance"])
    Transaction.objects.create(
        wallet=w, amount=winnings,
        tx_type="prediction_win",
        description=f"Kandege @{multiplier}x (Round #{bet.round_id})"
    )
    try:
        from notifications.utils import create_notification
        create_notification(
            bet.user, "wallet",
            f"Kandege: cashed out @{multiplier}x, won {winnings} pts",
            link="/user/arena?game=kandege"
        )
    except: pass


class KandegeRoundView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        r = get_or_create_round()
        advance_round(r)
        r.refresh_from_db()
        return Response(serialize_round(r, request.user))


class KandegeBetView(APIView):
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser, FormParser]

    def post(self, request):
        r = get_or_create_round()
        advance_round(r)
        r.refresh_from_db()

        if r.status != "waiting":
            # Try to find any waiting round
            r2 = KandegeRound.objects.filter(status="waiting").order_by("-created_at").first()
            if r2:
                r = r2
            else:
                # Create next round for betting
                r_next = KandegeRound.objects.create(
                    crash_point=generate_crash_point(), status="waiting"
                )
                r = r_next

        amount = int(request.data.get("amount", 0))
        auto_cashout = request.data.get("auto_cashout")
        if auto_cashout:
            auto_cashout = float(auto_cashout)

        if amount < 1:
            return Response({"error": "Min bet is 1 pt."}, status=400)
        # Check self-exclusion only
        from .models import KandegeLimit
        from django.utils import timezone as tz
        limit = KandegeLimit.objects.filter(user=request.user).first()
        if limit and limit.excluded:
            if not limit.excluded_until or tz.now() < limit.excluded_until:
                return Response({"error": "You have self-excluded from Kandege."}, status=403)

        from wallet.models import Wallet, Transaction
        w, _ = Wallet.objects.get_or_create(user=request.user)
        if w.balance < amount:
            return Response({"error": "Insufficient balance."}, status=400)
        w.balance -= amount
        w.save(update_fields=["balance"])
        Transaction.objects.create(
            wallet=w, amount=-amount,
            tx_type="game_bet",
            description=f"Kandege bet (Round #{r.id})"
        )
        KandegeBet.objects.create(
            round=r, user=request.user,
            amount=amount, auto_cashout=auto_cashout,
        )
        b = KandegeBet.objects.create(round=r, user=request.user, amount=amount, auto_cashout=auto_cashout)
        return Response({"status": "ok", "round_id": r.id, "amount": amount, "bet_id": b.id})


class KandegeCashoutView(APIView):
    permission_classes = [IsAuthenticated]
    parser_classes = [JSONParser, FormParser]

    def post(self, request):
        r = KandegeRound.objects.filter(status="flying").order_by("-created_at").first()
        if not r:
            return Response({"error": "No active round."}, status=400)
        advance_round(r)
        r.refresh_from_db()
        if r.status == "crashed":
            return Response({"error": "Too late — crashed!"}, status=400)
        bet_id = request.data.get("bet_id")
        if bet_id:
            bet = r.bets.filter(id=bet_id, user=request.user, cashed_out=False).first()
        else:
            bet = r.bets.filter(user=request.user, cashed_out=False).first()
        if not bet:
            return Response({"error": "No active bet."}, status=400)
        m = get_current_multiplier(r.started_at)
        process_cashout(bet, m)
        return Response({"status": "ok", "multiplier": m, "winnings": bet.winnings, "bet_id": bet.id})


class KandegeHistoryView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        rounds = KandegeRound.objects.filter(
            status="crashed"
        ).order_by("-crashed_at")[:20]
        return Response([{
            "id": r.id,
            "crash_point": r.crash_point,
        } for r in rounds])


class KandegeAdminView(APIView):
    """Admin dashboard for Kandege."""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        if not request.user.is_staff:
            return Response({"error": "Admin only."}, status=403)
        from django.db.models import Sum, Count
        from django.utils import timezone
        today = timezone.now().date()

        total_rounds  = KandegeRound.objects.filter(status="crashed").count()
        total_bets    = KandegeBet.objects.count()
        total_wagered = KandegeBet.objects.aggregate(t=Sum("amount"))["t"] or 0
        total_won     = KandegeBet.objects.filter(cashed_out=True).aggregate(t=Sum("winnings"))["t"] or 0
        house_edge    = total_wagered - total_won
        today_wagered = KandegeBet.objects.filter(created_at__date=today).aggregate(t=Sum("amount"))["t"] or 0

        recent_rounds = KandegeRound.objects.filter(status="crashed").order_by("-crashed_at")[:10]
        top_players = KandegeBet.objects.values("user__username").annotate(
            total=Sum("amount"), wins=Sum("winnings"), count=Count("id")
        ).order_by("-total")[:10]

        return Response({
            "stats": {
                "total_rounds": total_rounds,
                "total_bets": total_bets,
                "total_wagered": total_wagered,
                "total_won": total_won,
                "house_edge": house_edge,
                "today_wagered": today_wagered,
            },
            "recent_rounds": [{
                "id": r.id,
                "crash_point": r.crash_point,
                "crashed_at": r.crashed_at,
                "bets_count": r.bets.count(),
            } for r in recent_rounds],
            "top_players": list(top_players),
        })


class KandegeLimitView(APIView):
    """Get/set responsible gaming limits."""
    permission_classes = [IsAuthenticated]

    def get(self, request):
        from .models import KandegeLimit
        limit, _ = KandegeLimit.objects.get_or_create(user=request.user)
        return Response({
            "daily_limit": limit.daily_limit,
            "excluded": limit.excluded,
            "excluded_until": limit.excluded_until,
        })

    def post(self, request):
        from .models import KandegeLimit
        from django.utils import timezone
        limit, _ = KandegeLimit.objects.get_or_create(user=request.user)

        if "daily_limit" in request.data:
            limit.daily_limit = max(100, int(request.data["daily_limit"]))

        if request.data.get("self_exclude"):
            days = int(request.data.get("days", 7))
            limit.excluded = True
            limit.excluded_until = timezone.now() + timezone.timedelta(days=days)

        if request.data.get("resume"):
            if limit.excluded_until and timezone.now() > limit.excluded_until:
                limit.excluded = False
                limit.excluded_until = None
            else:
                return Response({"error": "Exclusion period not over yet."}, status=400)

        limit.save()
        return Response({"status": "updated", "daily_limit": limit.daily_limit, "excluded": limit.excluded})
