import random
from django.utils import timezone
from django.db.models import Sum, Q, F
from rest_framework import permissions
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Wallet, Transaction, Prediction, PredictionStake
from .utils import get_or_create_wallet, award_points
from account.models import User
from notifications.utils import create_notification


def _serialize_wallet(wallet):
    return {
        "balance": wallet.balance,
        "can_spin": wallet.can_spin(),
        "can_claim_login_bonus": wallet.can_claim_login_bonus(),
        "next_spin_at": (wallet.last_spin + timezone.timedelta(hours=24)).isoformat() if wallet.last_spin else None,
    }


# ── WALLET ──
class WalletView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        wallet = get_or_create_wallet(request.user)
        # Daily login bonus auto-claim
        if wallet.can_claim_login_bonus():
            wallet.balance += 10
            wallet.last_login_bonus = timezone.now().date()
            wallet.save(update_fields=["balance", "last_login_bonus"])
            Transaction.objects.create(wallet=wallet, amount=10, tx_type="daily_login", description="Daily login bonus")
        return Response(_serialize_wallet(wallet))


class TransactionListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        wallet = get_or_create_wallet(request.user)
        txs = wallet.transactions.all()[:30]
        return Response([{
            "id": t.id, "amount": t.amount, "tx_type": t.tx_type,
            "description": t.description, "created_at": t.created_at,
        } for t in txs])


# ── DAILY SPIN ──
SPIN_PRIZES = [
    {"label": "10 pts",  "amount": 10,  "weight": 30},
    {"label": "20 pts",  "amount": 20,  "weight": 25},
    {"label": "50 pts",  "amount": 50,  "weight": 18},
    {"label": "100 pts", "amount": 100, "weight": 12},
    {"label": "200 pts", "amount": 200, "weight": 8},
    {"label": "500 pts", "amount": 500, "weight": 5},
    {"label": "JACKPOT 1000 pts", "amount": 1000, "weight": 2},
]

class SpinView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        wallet = get_or_create_wallet(request.user)
        if not wallet.can_spin():
            next_at = wallet.last_spin + timezone.timedelta(hours=24)
            return Response({"error": "Already spun today.", "next_spin_at": next_at.isoformat()}, status=400)

        weights = [p["weight"] for p in SPIN_PRIZES]
        prize = random.choices(SPIN_PRIZES, weights=weights, k=1)[0]

        wallet.balance += prize["amount"]
        wallet.last_spin = timezone.now()
        wallet.save(update_fields=["balance", "last_spin"])
        Transaction.objects.create(wallet=wallet, amount=prize["amount"], tx_type="spin", description=f"Daily spin: {prize['label']}")

        return Response({
            "prize": prize["label"],
            "amount": prize["amount"],
            "new_balance": wallet.balance,
            "next_spin_at": (wallet.last_spin + timezone.timedelta(hours=24)).isoformat(),
        })


# ── LEADERBOARD ──
class LeaderboardView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        top = Wallet.objects.select_related("user").order_by("-balance")[:50]
        return Response([{
            "rank": i+1,
            "user_id": w.user.id,
            "username": w.user.username,
            "profile_image": request.build_absolute_uri(w.user.profile_image.url) if w.user.profile_image else None,
            "balance": w.balance,
        } for i, w in enumerate(top)])


# ── PREDICTIONS ──
def _serialize_prediction(p, request):
    user_stake = None
    if request.user.is_authenticated:
        s = p.stakes.filter(user=request.user).first()
        if s:
            user_stake = {"option": s.option, "amount": s.amount, "payout": s.payout}
    pool_a, pool_b = p.pool_a, p.pool_b
    total = pool_a + pool_b
    return {
        "id": p.id, "title": p.title, "description": p.description,
        "option_a": p.option_a, "option_b": p.option_b,
        "category": p.category, "status": p.status,
        "closes_at": p.closes_at, "winning_option": p.winning_option,
        "pool_a": pool_a, "pool_b": pool_b, "total_pool": total,
        "pct_a": round(pool_a/total*100) if total else 50,
        "pct_b": round(pool_b/total*100) if total else 50,
        "creator": p.creator.username,
        "creator_id": p.creator.id,
        "is_closed": p.closes_at <= timezone.now() or p.status != "open",
        "user_stake": user_stake,
        "created_at": p.created_at,
    }


class PredictionListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        qs = Prediction.objects.select_related("creator").prefetch_related("stakes")
        status_filter = request.query_params.get("status")
        if status_filter == "open":
            qs = qs.filter(status="open", closes_at__gt=timezone.now())
        elif status_filter == "settled":
            qs = qs.filter(status="settled")
        return Response([_serialize_prediction(p, request) for p in qs])

    def post(self, request):
        title    = request.data.get("title","").strip()
        opt_a    = request.data.get("option_a","").strip()
        opt_b    = request.data.get("option_b","").strip()
        closes_at = request.data.get("closes_at")
        if not title or not opt_a or not opt_b or not closes_at:
            return Response({"error":"Title, both options, and closing time required."}, status=400)
        p = Prediction.objects.create(
            creator=request.user, title=title,
            description=request.data.get("description","").strip(),
            option_a=opt_a, option_b=opt_b,
            category=request.data.get("category","general"),
            closes_at=closes_at,
        )
        return Response(_serialize_prediction(p, request), status=201)


class PredictionStakeView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        p = Prediction.objects.filter(pk=pk).first()
        if not p:
            return Response({"error":"Not found."}, status=404)
        if p.status != "open" or p.closes_at <= timezone.now():
            return Response({"error":"This prediction is closed."}, status=400)
        if p.stakes.filter(user=request.user).exists():
            return Response({"error":"You already predicted on this."}, status=400)

        option = request.data.get("option")
        amount = request.data.get("amount")
        if option not in ("a","b"):
            return Response({"error":"Invalid option."}, status=400)
        try:
            amount = int(amount)
        except (TypeError, ValueError):
            return Response({"error":"Invalid amount."}, status=400)
        if amount < 10:
            return Response({"error":"Minimum stake is 10 points."}, status=400)

        wallet = get_or_create_wallet(request.user)
        if wallet.balance < amount:
            return Response({"error":"Insufficient balance."}, status=400)

        wallet.balance -= amount
        wallet.save(update_fields=["balance"])
        Transaction.objects.create(wallet=wallet, amount=-amount, tx_type="prediction_stake", description=f"Staked on: {p.title}")

        PredictionStake.objects.create(prediction=p, user=request.user, option=option, amount=amount)
        return Response(_serialize_prediction(p, request), status=201)


class PredictionSettleView(APIView):
    """Creator or staff settles the prediction by choosing winning option."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        p = Prediction.objects.filter(pk=pk).first()
        if not p:
            return Response({"error":"Not found."}, status=404)
        if not request.user.is_staff:
            return Response({"error":"Only admins can settle predictions."}, status=403)
        if p.status == "settled":
            return Response({"error":"Already settled."}, status=400)

        winning = request.data.get("winning_option")
        if winning not in ("a","b"):
            return Response({"error":"Invalid winning option."}, status=400)

        total_pool = p.total_pool
        winning_pool = p.pool_a if winning=="a" else p.pool_b
        losing_pool  = p.total_pool - winning_pool

        for stake in p.stakes.all():
            wallet = get_or_create_wallet(stake.user)
            if stake.option == winning:
                # Winner gets stake back + share of losing pool proportional to their stake
                share = (stake.amount / winning_pool * losing_pool) if winning_pool else 0
                payout = int(stake.amount + share)
                stake.payout = payout
                stake.save(update_fields=["payout"])
                wallet.balance += payout
                wallet.save(update_fields=["balance"])
                Transaction.objects.create(wallet=wallet, amount=payout, tx_type="prediction_win", description=f"Won: {p.title}")
                create_notification(stake.user, "prediction_win", f"You won {payout} points on '{p.title}'! 🎉", request.user)
            else:
                Transaction.objects.create(wallet=wallet, amount=0, tx_type="prediction_loss", description=f"Lost: {p.title}")
                create_notification(stake.user, "prediction_loss", f"You lost your stake on '{p.title}'.", request.user)

        p.status = "settled"
        p.winning_option = winning
        p.save(update_fields=["status","winning_option"])
        return Response(_serialize_prediction(p, request))


class PredictionRefundView(APIView):
    """Staff-only: refund all stakes for a prediction with no clear settlement (cancels it)."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        p = Prediction.objects.filter(pk=pk).first()
        if not p:
            return Response({"error":"Not found."}, status=404)
        if not request.user.is_staff:
            return Response({"error":"Only admins can refund predictions."}, status=403)
        if p.status == "settled":
            return Response({"error":"Already settled."}, status=400)

        for stake in p.stakes.all():
            wallet = get_or_create_wallet(stake.user)
            wallet.balance += stake.amount
            wallet.save(update_fields=["balance"])
            stake.payout = stake.amount
            stake.save(update_fields=["payout"])
            Transaction.objects.create(wallet=wallet, amount=stake.amount, tx_type="prediction_win", description=f"Refund: {p.title}")
            create_notification(stake.user, "prediction_refund", f"Your stake on '{p.title}' was refunded.", request.user)

        p.status = "cancelled"
        p.save(update_fields=["status"])
        return Response(_serialize_prediction(p, request))


class StalePredictionsView(APIView):
    """Returns predictions that closed but haven't been settled (for admin review)."""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        if not request.user.is_staff:
            return Response({"error":"Not authorized."}, status=403)
        qs = Prediction.objects.filter(status="open", closes_at__lte=timezone.now()).select_related("creator").prefetch_related("stakes")
        return Response([_serialize_prediction(p, request) for p in qs])


# ── CARD DUELS ──
import random as _random
from .models import Duel

CARD_NAMES = [
    "🦁 Lion", "🐆 Cheetah", "🦅 Eagle", "🐘 Elephant", "�� Rhino",
    "🐊 Crocodile", "🦒 Giraffe", "🐍 Cobra", "🦍 Gorilla", "🐅 Tiger",
]

def _draw_hand():
    """Draw 5 random cards, each with Strength/Speed/Luck stats 1-10."""
    hand = []
    for _ in range(5):
        hand.append({
            "name": _random.choice(CARD_NAMES),
            "strength": _random.randint(1,10),
            "speed":    _random.randint(1,10),
            "luck":     _random.randint(1,10),
        })
    return hand


STAT_ORDER = ["strength","speed","luck"]

def _serialize_duel(d, request):
    is_challenger = request.user.id == d.challenger_id
    my_hand    = d.hand_c if is_challenger else d.hand_o
    my_order   = d.order_c if is_challenger else d.order_o
    opp_order  = d.order_o if is_challenger else d.order_c
    return {
        "id": d.id, "stake": d.stake, "status": d.status,
        "challenger": d.challenger.username, "challenger_id": d.challenger.id,
        "opponent": d.opponent.username, "opponent_id": d.opponent.id,
        "winner": d.winner.username if d.winner else None,
        "winner_id": d.winner.id if d.winner else None,
        "rounds": d.rounds,
        "my_hand": my_hand,
        "my_order_submitted": my_order is not None,
        "opp_order_submitted": opp_order is not None,
        "created_at": d.created_at, "resolved_at": d.resolved_at,
    }


class DuelListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        """List duels involving the current user."""
        qs = Duel.objects.filter(Q(challenger=request.user)|Q(opponent=request.user)).select_related("challenger","opponent","winner")
        status_filter = request.query_params.get("status")
        if status_filter:
            qs = qs.filter(status=status_filter)
        return Response([_serialize_duel(d, request) for d in qs[:50]])

    def post(self, request):
        """Challenge a user to a duel."""
        opponent_id = request.data.get("opponent_id")
        stake       = request.data.get("stake")
        try:
            stake = int(stake)
        except (TypeError, ValueError):
            return Response({"error":"Invalid stake."}, status=400)
        if stake < 10:
            return Response({"error":"Minimum stake is 10 points."}, status=400)

        opponent = User.objects.filter(pk=opponent_id).first()
        if not opponent:
            return Response({"error":"Opponent not found."}, status=404)
        if opponent == request.user:
            return Response({"error":"You cannot duel yourself."}, status=400)

        wallet = get_or_create_wallet(request.user)
        if wallet.balance < stake:
            return Response({"error":"Insufficient balance."}, status=400)

        opp_wallet = get_or_create_wallet(opponent)
        if opp_wallet.balance < stake:
            return Response({"error":f"{opponent.username} doesn't have enough points for this stake."}, status=400)

        d = Duel.objects.create(challenger=request.user, opponent=opponent, stake=stake)
        create_notification(opponent, "duel_challenge", f"{request.user.username} challenged you to a Card Duel for {stake} pts! 🃏", link="/user/arena", sender=request.user)
        return Response(_serialize_duel(d, request), status=201)


class DuelRespondView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        d = Duel.objects.filter(pk=pk).first()
        if not d:
            return Response({"error":"Not found."}, status=404)
        if d.opponent != request.user:
            return Response({"error":"Not authorized."}, status=403)
        if d.status != "pending":
            return Response({"error":"This duel is no longer pending."}, status=400)

        action = request.data.get("action")
        if action == "decline":
            d.status = "declined"
            d.save(update_fields=["status"])
            create_notification(d.challenger, "duel_declined", f"{request.user.username} declined your duel challenge.", link="/user/arena", sender=request.user)
            return Response(_serialize_duel(d, request))

        if action != "accept":
            return Response({"error":"Invalid action."}, status=400)

        # Re-check balances at accept time
        c_wallet = get_or_create_wallet(d.challenger)
        o_wallet = get_or_create_wallet(d.opponent)
        if c_wallet.balance < d.stake or o_wallet.balance < d.stake:
            d.status = "expired"
            d.save(update_fields=["status"])
            return Response({"error":"One of you no longer has enough points."}, status=400)

        # Deduct stakes upfront
        c_wallet.balance -= d.stake; c_wallet.save(update_fields=["balance"])
        o_wallet.balance -= d.stake; o_wallet.save(update_fields=["balance"])
        Transaction.objects.create(wallet=c_wallet, amount=-d.stake, tx_type="prediction_stake", description=f"Duel stake vs {d.opponent.username}")
        Transaction.objects.create(wallet=o_wallet, amount=-d.stake, tx_type="prediction_stake", description=f"Duel stake vs {d.challenger.username}")

        # Draw hands — move to drafting phase (players pick card order)
        d.hand_c = _draw_hand()
        d.hand_o = _draw_hand()
        d.status = "drafting"
        d.save(update_fields=["hand_c","hand_o","status"])

        create_notification(d.challenger, "duel_drafting", f"{d.opponent.username} accepted! Choose your cards. 🃏", link="/user/arena")

        return Response(_serialize_duel(d, request))


def _resolve_duel(d, request):
    """Resolve a duel once both players have submitted their card order."""
    hand_c, hand_o = d.hand_c, d.hand_o
    order_c, order_o = d.order_c, d.order_o

    rounds = []
    c_wins, o_wins = 0, 0
    for i in range(3):
        stat = STAT_ORDER[i]
        card_c = hand_c[order_c[i]]
        card_o = hand_o[order_o[i]]
        val_c, val_o = card_c[stat], card_o[stat]
        if val_c > val_o: c_wins += 1; round_winner = "challenger"
        elif val_o > val_c: o_wins += 1; round_winner = "opponent"
        else: round_winner = "draw"
        rounds.append({
            "round": i+1, "stat": stat,
            "card_c": card_c, "card_o": card_o,
            "val_c": val_c, "val_o": val_o,
            "winner": round_winner,
        })

    c_wallet = get_or_create_wallet(d.challenger)
    o_wallet = get_or_create_wallet(d.opponent)
    pot = d.stake * 2

    if c_wins > o_wins:
        winner_user, winner_wallet = d.challenger, c_wallet
    elif o_wins > c_wins:
        winner_user, winner_wallet = d.opponent, o_wallet
    else:
        winner_user, winner_wallet = None, None

    if winner_user:
        winner_wallet.balance += pot
        winner_wallet.save(update_fields=["balance"])
        Transaction.objects.create(wallet=winner_wallet, amount=pot, tx_type="prediction_win", description=f"Duel won! ({c_wins}-{o_wins})")
        loser = d.opponent if winner_user==d.challenger else d.challenger
        create_notification(winner_user, "duel_won", f"You won the duel and earned {pot} pts! 🏆", link="/user/arena")
        create_notification(loser, "duel_lost", f"You lost the duel ({c_wins}-{o_wins}).", link="/user/arena")
    else:
        c_wallet.balance += d.stake; c_wallet.save(update_fields=["balance"])
        o_wallet.balance += d.stake; o_wallet.save(update_fields=["balance"])
        Transaction.objects.create(wallet=c_wallet, amount=d.stake, tx_type="prediction_win", description="Duel draw - refunded")
        Transaction.objects.create(wallet=o_wallet, amount=d.stake, tx_type="prediction_win", description="Duel draw - refunded")
        create_notification(d.challenger, "duel_draw", "Your duel ended in a draw — stakes refunded.", link="/user/arena")
        create_notification(d.opponent, "duel_draw", "Your duel ended in a draw — stakes refunded.", link="/user/arena")

    d.status = "finished"
    d.winner = winner_user
    d.rounds = rounds
    d.resolved_at = timezone.now()
    d.save(update_fields=["status","winner","rounds","resolved_at"])


class DuelSubmitOrderView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, pk):
        d = Duel.objects.filter(pk=pk).first()
        if not d:
            return Response({"error":"Not found."}, status=404)
        if request.user.id not in (d.challenger_id, d.opponent_id):
            return Response({"error":"Not authorized."}, status=403)
        if d.status != "drafting":
            return Response({"error":"Duel is not in drafting phase."}, status=400)

        order = request.data.get("order")
        if (not isinstance(order, list) or len(order) != 3
                or len(set(order)) != 3
                or not all(isinstance(i,int) and 0<=i<=4 for i in order)):
            return Response({"error":"Order must be 3 unique card indices (0-4 from your 5 cards)."}, status=400)

        is_challenger = request.user.id == d.challenger_id
        if is_challenger:
            if d.order_c is not None:
                return Response({"error":"You already submitted your order."}, status=400)
            d.order_c = order
            d.save(update_fields=["order_c"])
            other_user = d.opponent
        else:
            if d.order_o is not None:
                return Response({"error":"You already submitted your order."}, status=400)
            d.order_o = order
            d.save(update_fields=["order_o"])
            other_user = d.challenger

        if d.order_c is not None and d.order_o is not None:
            _resolve_duel(d, request)
        else:
            create_notification(other_user, "duel_drafting", "Your opponent picked their cards — your turn! 🃏", link="/user/arena")

        return Response(_serialize_duel(d, request))


# ── BOOST ──
BOOST_COST = 100
BOOST_HOURS = 24

class BoostView(APIView):
    """Spend points to feature a post or business listing for 24h."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        target_type = request.data.get("target_type")  # 'post' or 'business'
        target_id   = request.data.get("target_id")

        wallet = get_or_create_wallet(request.user)
        if wallet.balance < BOOST_COST:
            return Response({"error": f"You need {BOOST_COST} points to boost. Your balance: {wallet.balance}."}, status=400)

        if target_type == "post":
            from posts.models import Post
            obj = Post.objects.filter(pk=target_id, author=request.user).first()
            if not obj:
                return Response({"error":"Post not found or not yours."}, status=404)
        elif target_type == "business":
            from community.models import BusinessProfile
            obj = BusinessProfile.objects.filter(pk=target_id, owner=request.user).first()
            if not obj:
                return Response({"error":"Business not found or not yours."}, status=404)
        else:
            return Response({"error":"Invalid target_type."}, status=400)

        now = timezone.now()
        current = obj.boosted_until
        base = current if (current and current > now) else now
        obj.boosted_until = base + timezone.timedelta(hours=BOOST_HOURS)
        obj.save(update_fields=["boosted_until"])

        wallet.balance -= BOOST_COST
        wallet.save(update_fields=["balance"])
        Transaction.objects.create(wallet=wallet, amount=-BOOST_COST, tx_type="boost", description=f"Boosted {target_type} #{target_id}")

        return Response({
            "boosted_until": obj.boosted_until,
            "new_balance": wallet.balance,
            "cost": BOOST_COST,
        })


# ── COIN FLIP ──
import random as _rand2
from .models import CoinFlip

class CoinFlipView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        history = CoinFlip.objects.filter(user=request.user)[:20]
        return Response([{
            "id": f.id, "stake": f.stake, "choice": f.choice,
            "result": f.result, "won": f.won, "payout": f.payout,
            "created_at": f.created_at,
        } for f in history])

    def post(self, request):
        choice = request.data.get("choice", "").lower()
        if choice not in ("heads", "tails"):
            return Response({"error": "Choose heads or tails."}, status=400)
        try:
            stake = int(request.data.get("stake", 0))
        except (TypeError, ValueError):
            return Response({"error": "Invalid stake."}, status=400)
        if stake < 10:
            return Response({"error": "Minimum stake is 10 points."}, status=400)

        wallet = get_or_create_wallet(request.user)
        if wallet.balance < stake:
            return Response({"error": f"Insufficient balance. You have {wallet.balance} pts."}, status=400)

        result = _rand2.choice(["heads", "tails"])
        won = choice == result
        payout = stake * 2 if won else 0

        wallet.balance = wallet.balance - stake + payout
        wallet.save(update_fields=["balance"])

        Transaction.objects.create(
            wallet=wallet,
            amount=payout - stake,
            tx_type="prediction_win" if won else "prediction_loss",
            description=f"Coin flip: {choice} vs {result} — {'won' if won else 'lost'} {stake}pts"
        )

        flip = CoinFlip.objects.create(
            user=request.user, stake=stake, choice=choice,
            result=result, won=won, payout=payout
        )

        return Response({
            "id": flip.id, "stake": stake, "choice": choice,
            "result": result, "won": won, "payout": payout,
            "new_balance": wallet.balance,
        })


# ── TIC TAC TOE ──
from .models import TicTacToe

class TicTacToeListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        games = TicTacToe.objects.filter(
            Q(player_x=request.user)|Q(player_o=request.user)
        ).select_related("player_x","player_o")[:20]
        return Response([{
            "id": g.id, "stake": g.stake, "status": g.status,
            "board": g.board, "current": g.current, "winner": g.winner,
            "player_x": g.player_x.username, "player_x_id": g.player_x.id,
            "player_o": g.player_o.username if g.player_o else None,
            "player_o_id": g.player_o.id if g.player_o else None,
            "created_at": g.created_at,
            "rematch_of": g.rematch_of,
        } for g in games])

    def post(self, request):
        stake = int(request.data.get("stake", 0))
        if stake < 0:
            return Response({"error": "Invalid stake."}, status=400)
        wallet = get_or_create_wallet(request.user)
        if stake > 0 and wallet.balance < stake:
            return Response({"error": "Insufficient balance."}, status=400)
        if stake > 0:
            wallet.balance -= stake
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-stake, tx_type="prediction_stake", description=f"TTT stake")
        rematch_of = request.data.get("rematch_of")
        game = TicTacToe.objects.create(
            player_x=request.user, stake=stake,
            board=[""] * 9, status="waiting",
            rematch_of=rematch_of,
        )
        return Response({
            "id": game.id, "stake": game.stake, "status": game.status,
            "board": game.board, "current": game.current,
            "player_x": game.player_x.username, "player_x_id": game.player_x.id,
            "player_o": None, "player_o_id": None,
        }, status=201)


class TicTacToeOpenView(APIView):
    """List open games anyone can join."""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        games = TicTacToe.objects.filter(
            status="waiting"
        ).exclude(player_x=request.user).select_related("player_x")[:20]
        return Response([{
            "id": g.id, "stake": g.stake,
            "player_x": g.player_x.username,
            "created_at": g.created_at,
            "rematch_of": g.rematch_of,
        } for g in games])


class TicTacToeJoinView(APIView):
    """REST endpoint to join a game — simpler than WS join."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import TicTacToe
        game = TicTacToe.objects.filter(id=game_id, status="waiting").first()
        if not game:
            return Response({"error": "Game not found or already started."}, status=404)
        if game.player_x == request.user:
            return Response({"error": "You created this game."}, status=400)

        wallet = get_or_create_wallet(request.user)
        if game.stake > 0 and wallet.balance < game.stake:
            return Response({"error": "Insufficient balance."}, status=400)

        if game.stake > 0:
            wallet.balance -= game.stake
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-game.stake, tx_type="prediction_stake", description=f"TTT join #{game.id}")

        game.player_o = request.user
        game.status = "playing"
        game.board = [""] * 9
        game.save()

        # Notify via WS
        try:
            from channels.layers import get_channel_layer
            from asgiref.sync import async_to_sync
            channel_layer = get_channel_layer()
            data = {
                "id": game.id, "board": game.board, "current": game.current,
                "status": game.status, "winner": game.winner, "stake": game.stake,
                "player_x": game.player_x.username, "player_x_id": game.player_x.id,
                "player_o": game.player_o.username, "player_o_id": game.player_o.id,
            }
            async_to_sync(channel_layer.group_send)(
                f"ttt_{game.id}",
                {"type": "game.update", "game": data}
            )
        except Exception as e:
            print(f"WS notify error: {e}")

        return Response({
            "id": game.id, "board": game.board, "current": game.current,
            "status": game.status, "stake": game.stake,
            "player_x": game.player_x.username, "player_x_id": game.player_x.id,
            "player_o": game.player_o.username, "player_o_id": game.player_o.id,
            "winner": None,
        })


class TicTacToeMoveView(APIView):
    """REST endpoint to make a move."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import TicTacToe
        game = TicTacToe.objects.filter(id=game_id, status="playing").first()
        if not game:
            return Response({"error": "Game not found."}, status=404)

        is_x = game.player_x == request.user
        is_o = game.player_o == request.user
        if not (is_x or is_o):
            return Response({"error": "Not a player."}, status=403)

        my_mark = "X" if is_x else "O"
        if game.current != my_mark:
            return Response({"error": "Not your turn."}, status=400)

        index = request.data.get("index")
        if index is None or not (0 <= index <= 8):
            return Response({"error": "Invalid index."}, status=400)

        board = list(game.board)
        if board[index]:
            return Response({"error": "Cell taken."}, status=400)

        board[index] = my_mark
        game.board = board

        # Check winner
        WINS = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
        winner = None
        for combo in WINS:
            a,b,c = combo
            if board[a] and board[a]==board[b]==board[c]:
                winner = board[a]; break
        if not winner and all(board):
            winner = "D"

        if winner:
            game.status = "finished"
            game.winner = winner
            pot = game.stake * 2
            if winner == "D":
                for p in [game.player_x, game.player_o]:
                    w = get_or_create_wallet(p)
                    w.balance += game.stake
                    w.save(update_fields=["balance"])
                    Transaction.objects.create(wallet=w, amount=game.stake, tx_type="prediction_win", description=f"TTT draw #{game.id}")
            else:
                win_user = game.player_x if winner=="X" else game.player_o
                lose_user = game.player_o if winner=="X" else game.player_x
                w = get_or_create_wallet(win_user)
                w.balance += pot
                w.save(update_fields=["balance"])
                Transaction.objects.create(wallet=w, amount=pot, tx_type="prediction_win", description=f"TTT win #{game.id}")
        else:
            game.current = "O" if my_mark=="X" else "X"

        game.save()

        data = {
            "id": game.id, "board": game.board, "current": game.current,
            "status": game.status, "winner": game.winner, "stake": game.stake,
            "player_x": game.player_x.username, "player_x_id": game.player_x.id,
            "player_o": game.player_o.username if game.player_o else None,
            "player_o_id": game.player_o.id if game.player_o else None,
        }

        # Notify via WS channel
        try:
            from channels.layers import get_channel_layer
            from asgiref.sync import async_to_sync
            async_to_sync(get_channel_layer().group_send)(
                f"ttt_{game.id}", {"type": "game.update", "game": data}
            )
        except Exception as e:
            print(f"WS error: {e}")

        return Response(data)


class TicTacToeDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, game_id):
        from .models import TicTacToe
        game = TicTacToe.objects.filter(
            id=game_id
        ).filter(
            Q(player_x=request.user)|Q(player_o=request.user)|Q(status="waiting")
        ).select_related("player_x","player_o").first()
        if not game:
            return Response({"error":"Not found"},status=404)
        return Response({
            "id":game.id,"board":game.board,"current":game.current,
            "status":game.status,"winner":game.winner,"stake":game.stake,
            "player_x":game.player_x.username,"player_x_id":game.player_x.id,
            "player_o":game.player_o.username if game.player_o else None,
            "player_o_id":game.player_o.id if game.player_o else None,
            "rematch_of":game.rematch_of,
        })


# ── CHECKERS LOGIC ──
def init_checkers_board():
    # 0=empty, 'r'=red, 'R'=red king, 'b'=black, 'B'=black king
    board = [None] * 64
    for row in range(8):
        for col in range(8):
            idx = row * 8 + col
            if (row + col) % 2 == 1:
                if row < 3:
                    board[idx] = 'b'
                elif row > 4:
                    board[idx] = 'r'
    return board

def get_checkers_moves(board, color):
    """Return all valid moves for color as list of (from, to, captures)."""
    moves = []
    jumps = []
    dirs_r = [(-1, -1), (-1, 1)]  # red moves up
    dirs_b = [(1, -1),  (1, 1)]   # black moves down
    dirs_k = [(-1,-1),(-1,1),(1,-1),(1,1)]

    pieces = []
    for i, p in enumerate(board):
        if p and p.lower() == color.lower():
            pieces.append(i)

    for idx in pieces:
        row, col = divmod(idx, 8)
        piece = board[idx]
        is_king = piece.isupper()
        dirs = dirs_k if is_king else (dirs_r if color == 'r' else dirs_b)

        for dr, dc in dirs:
            nr, nc = row + dr, col + dc
            if 0 <= nr < 8 and 0 <= nc < 8:
                nidx = nr * 8 + nc
                if board[nidx] is None:
                    moves.append((idx, nidx, []))
                elif board[nidx].lower() != color.lower():
                    # Check jump
                    jr, jc = nr + dr, nc + dc
                    if 0 <= jr < 8 and 0 <= jc < 8:
                        jidx = jr * 8 + jc
                        if board[jidx] is None:
                            jumps.append((idx, jidx, [nidx]))

    return jumps if jumps else moves

def apply_checkers_move(board, from_idx, to_idx, captures):
    board = list(board)
    piece = board[from_idx]
    board[from_idx] = None
    for cap in captures:
        board[cap] = None
    # King promotion
    row = to_idx // 8
    if piece == 'r' and row == 0:
        piece = 'R'
    elif piece == 'b' and row == 7:
        piece = 'B'
    board[to_idx] = piece
    return board

def check_checkers_winner(board, current_color):
    next_color = 'b' if current_color == 'r' else 'r'
    moves = get_checkers_moves(board, next_color)
    if not moves:
        return current_color.upper()
    r_pieces = [p for p in board if p and p.lower() == 'r']
    b_pieces = [p for p in board if p and p.lower() == 'b']
    if not r_pieces: return 'B'
    if not b_pieces: return 'R'
    return None


def serialize_checkers(g):
    return {
        "id": g.id, "board": g.board, "current": g.current,
        "status": g.status, "winner": g.winner, "stake": g.stake,
        "player_r": g.player_r.username, "player_r_id": g.player_r.id,
        "player_b": g.player_b.username if g.player_b else None,
        "player_b_id": g.player_b.id if g.player_b else None,
        "rematch_of": g.rematch_of,
    }


class CheckersListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        from .models import Checkers
        games = Checkers.objects.filter(
            Q(player_r=request.user)|Q(player_b=request.user)
        ).select_related("player_r","player_b")[:20]
        return Response([serialize_checkers(g) for g in games])

    def post(self, request):
        from .models import Checkers
        stake = int(request.data.get("stake", 0))
        rematch_of = request.data.get("rematch_of")
        wallet = get_or_create_wallet(request.user)
        if stake > 0 and wallet.balance < stake:
            return Response({"error": "Insufficient balance."}, status=400)
        if stake > 0:
            wallet.balance -= stake
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-stake, tx_type="prediction_stake", description=f"Checkers stake")
        game = Checkers.objects.create(
            player_r=request.user, stake=stake,
            board=init_checkers_board(), status="waiting",
            rematch_of=rematch_of,
        )
        return Response(serialize_checkers(game), status=201)


class CheckersOpenView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        from .models import Checkers
        games = Checkers.objects.filter(
            status="waiting"
        ).exclude(player_r=request.user).select_related("player_r")[:20]
        return Response([serialize_checkers(g) for g in games])


class CheckersDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, game_id):
        from .models import Checkers
        game = Checkers.objects.filter(id=game_id).select_related("player_r","player_b").first()
        if not game:
            return Response({"error": "Not found"}, status=404)
        return Response(serialize_checkers(game))


class CheckersJoinView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import Checkers
        game = Checkers.objects.filter(id=game_id, status="waiting").first()
        if not game:
            return Response({"error": "Game not found."}, status=404)
        if game.player_r == request.user:
            return Response({"error": "You created this game."}, status=400)
        wallet = get_or_create_wallet(request.user)
        if game.stake > 0 and wallet.balance < game.stake:
            return Response({"error": "Insufficient balance."}, status=400)
        if game.stake > 0:
            wallet.balance -= game.stake
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-game.stake, tx_type="prediction_stake", description=f"Checkers join #{game.id}")
        game.player_b = request.user
        game.status = "playing"
        game.save()
        return Response(serialize_checkers(game))


class CheckersMoveView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import Checkers
        game = Checkers.objects.filter(id=game_id, status="playing").select_related("player_r","player_b").first()
        if not game:
            return Response({"error": "Game not found."}, status=404)
        is_r = game.player_r == request.user
        is_b = game.player_b == request.user
        if not (is_r or is_b):
            return Response({"error": "Not a player."}, status=403)
        my_color = 'r' if is_r else 'b'
        if game.current.lower() != my_color.lower():
            return Response({"error": "Not your turn."}, status=400)

        from_idx = request.data.get("from_idx")
        to_idx   = request.data.get("to_idx")
        if from_idx is None or to_idx is None:
            return Response({"error": "from_idx and to_idx required."}, status=400)

        # Validate move
        valid_moves = get_checkers_moves(game.board, my_color)
        move = next((m for m in valid_moves if m[0]==from_idx and m[1]==to_idx), None)
        if not move:
            return Response({"error": "Invalid move."}, status=400)

        board = apply_checkers_move(game.board, from_idx, to_idx, move[2])
        game.board = board

        winner = check_checkers_winner(board, my_color)
        if winner:
            game.status = "finished"
            game.winner = winner
            pot = game.stake * 2
            win_user = game.player_r if winner == 'R' else game.player_b
            lose_user = game.player_b if winner == 'R' else game.player_r
            if game.stake > 0:
                w = get_or_create_wallet(win_user)
                w.balance += pot
                w.save(update_fields=["balance"])
                Transaction.objects.create(wallet=w, amount=pot, tx_type="prediction_win", description=f"Checkers win #{game.id}")
        else:
            game.current = 'b' if my_color.lower() == 'r' else 'r'

        game.save()
        return Response(serialize_checkers(game))


# ── MAUA GAME LOGIC (Kenyan Rules) ──
import random as _rand3

SUITS = ["♠", "♥", "♦", "♣"]
RANKS = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
CARD_VALUES = {"A":15,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,"J":10,"Q":10,"K":10}
SPECIAL_CARDS = {"2", "3", "A", "J", "K", "Q"}  # cannot finish with these
DEAL_COUNT = 4  # deal 4 cards each

def make_deck():
    deck = [f"{r}{s}" for s in SUITS for r in RANKS]
    _rand3.shuffle(deck)
    return deck

def card_rank(c): return c[:-1] if len(c) > 1 else c[0]
def card_suit(c): return c[-1]
def card_value(c): return CARD_VALUES.get(card_rank(c), 10)

def is_special(card):
    return card_rank(card) in SPECIAL_CARDS

def can_play(card, top_card, current_suit, draw_stack=0):
    r = card_rank(card)
    # Ace cancels draw penalty or acts as wild
    if r == "A": return True
    # Q/8 = question card (suit changer) - can always play
    if r == "Q": return True
    # If draw stack pending, only 2 or 3 can counter
    if draw_stack > 0:
        return r in ("2", "3")
    suit = current_suit or card_suit(top_card)
    return card_suit(card) == suit or r == card_rank(top_card)

def serialize_maua(game, user_id=None):
    from .models import MauaPlayer
    players_data = []
    for ps in game.player_states.select_related("user").all():
        players_data.append({
            "user_id": ps.user.id,
            "username": ps.user.username,
            "hand_count": len(ps.hand),
            "hand": ps.hand if str(ps.user.id) == str(user_id) else [],
            "score": ps.score,
            "position": ps.position,
        })
    current_user_id = game.turn_order[game.current_idx] if game.turn_order else None
    top_card = game.discard[-1] if game.discard else None
    return {
        "id": game.id,
        "status": game.status,
        "stake": game.stake,
        "max_players": game.max_players,
        "player_count": game.players.count(),
        "players": players_data,
        "turn_order": game.turn_order,
        "current_player_id": current_user_id,
        "direction": game.direction,
        "draw_stack": game.draw_stack,
        "top_card": top_card,
        "current_suit": game.current_suit or (card_suit(top_card) if top_card else ""),
        "deck_count": len(game.deck),
        "winner_id": game.winner_id,
        "creator_id": game.creator_id,
    }


class MauaListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        from .models import MauaGame
        games = MauaGame.objects.filter(
            players=request.user
        ).prefetch_related("players","player_states__user")[:10]
        return Response([serialize_maua(g, request.user.id) for g in games])

    def post(self, request):
        from .models import MauaGame
        stake = int(request.data.get("stake", 0))
        max_players = int(request.data.get("max_players", 4))
        if max_players < 2 or max_players > 4:
            return Response({"error": "2-4 players only."}, status=400)
        wallet = get_or_create_wallet(request.user)
        if stake > 0 and wallet.balance < stake:
            return Response({"error": "Insufficient balance."}, status=400)
        if stake > 0:
            wallet.balance -= stake
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-stake, tx_type="prediction_stake", description="Maua stake")
        game = MauaGame.objects.create(
            creator=request.user, stake=stake, max_players=max_players,
        )
        game.players.add(request.user)
        from .models import MauaPlayer
        MauaPlayer.objects.create(game=game, user=request.user, hand=[])
        return Response(serialize_maua(game, request.user.id), status=201)


class MauaOpenView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        from .models import MauaGame
        games = MauaGame.objects.filter(status="waiting").exclude(
            players=request.user
        ).prefetch_related("players","player_states__user")[:20]
        return Response([serialize_maua(g) for g in games])


class MauaDetailView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, game_id):
        from .models import MauaGame
        game = MauaGame.objects.filter(id=game_id).prefetch_related("players","player_states__user").first()
        if not game:
            return Response({"error": "Not found"}, status=404)
        return Response(serialize_maua(game, request.user.id))


class MauaJoinView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import MauaGame, MauaPlayer
        game = MauaGame.objects.filter(id=game_id, status="waiting").prefetch_related("players").first()
        if not game:
            return Response({"error": "Game not found."}, status=404)
        if game.players.filter(id=request.user.id).exists():
            return Response({"error": "Already joined."}, status=400)
        if game.players.count() >= game.max_players:
            return Response({"error": "Game is full."}, status=400)
        wallet = get_or_create_wallet(request.user)
        if game.stake > 0 and wallet.balance < game.stake:
            return Response({"error": "Insufficient balance."}, status=400)
        if game.stake > 0:
            wallet.balance -= game.stake
            wallet.save(update_fields=["balance"])
            Transaction.objects.create(wallet=wallet, amount=-game.stake, tx_type="prediction_stake", description=f"Maua join #{game.id}")
        game.players.add(request.user)
        MauaPlayer.objects.create(game=game, user=request.user, hand=[])
        return Response(serialize_maua(game, request.user.id))


class MauaStartView(APIView):
    """Creator starts the game when enough players joined."""
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import MauaGame, MauaPlayer
        game = MauaGame.objects.filter(id=game_id, status="waiting", creator=request.user).prefetch_related("players").first()
        if not game:
            return Response({"error": "Not found or not creator."}, status=404)
        player_count = game.players.count()
        if player_count < 2:
            return Response({"error": "Need at least 2 players."}, status=400)

        deck = make_deck()
        players = list(game.players.all())
        _rand3.shuffle(players)
        turn_order = [p.id for p in players]
        hands = {}
        for p in players:
            hand = deck[:DEAL_COUNT]; deck = deck[DEAL_COUNT:]
            hands[str(p.id)] = hand
            MauaPlayer.objects.filter(game=game, user=p).update(hand=hand)

        # First card — must be a normal number card (4-10)
        top = deck.pop(0)
        while card_rank(top) in ["A","2","3","J","K","Q"]:
            deck.append(top)
            top = deck.pop(0)

        game.deck = deck
        game.discard = [top]
        game.turn_order = turn_order
        game.current_idx = 0
        game.direction = 1
        game.draw_stack = 0
        game.current_suit = card_suit(top)
        game.status = "playing"
        game.save()
        return Response(serialize_maua(game, request.user.id))


class MauaMoveView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, game_id):
        from .models import MauaGame, MauaPlayer
        game = MauaGame.objects.filter(id=game_id, status="playing").prefetch_related("players","player_states__user").first()
        if not game:
            return Response({"error": "Game not found."}, status=404)

        current_player_id = game.turn_order[game.current_idx]
        if str(request.user.id) != str(current_player_id):
            return Response({"error": "Not your turn."}, status=400)

        action = request.data.get("action")  # "play" or "draw"
        ps = MauaPlayer.objects.get(game=game, user=request.user)
        hand = list(ps.hand)
        deck = list(game.deck)
        discard = list(game.discard)
        top = discard[-1]
        draw_stack = game.draw_stack
        direction = game.direction
        turn_order = game.turn_order
        n = len(turn_order)
        current_suit = game.current_suit

        if action == "draw":
            draw_n = draw_stack if draw_stack > 0 else 1
            drawn = []
            for _ in range(draw_n):
                if not deck:
                    deck = discard[:-1]; _rand3.shuffle(deck); discard = [discard[-1]]
                if deck:
                    drawn.append(deck.pop(0))
            hand.extend(drawn)
            draw_stack = 0
            # Advance turn
            game.current_idx = (game.current_idx + direction) % n
            ps.hand = hand
            ps.save(update_fields=["hand"])
            game.deck = deck; game.discard = discard; game.draw_stack = draw_stack
            game.save()
            return Response(serialize_maua(game, request.user.id))

        if action == "play":
            card = request.data.get("card")
            chosen_suit = request.data.get("suit", "")
            if not card or card not in hand:
                return Response({"error": "Card not in hand."}, status=400)

            # If draw stack pending, must play a 2 or draw
            r = card_rank(card)
            if draw_stack > 0 and r != "2":
                return Response({"error": f"Must play a 2 or draw {draw_stack} cards."}, status=400)

            if not can_play(card, top, current_suit, draw_stack):
                return Response({"error": "Card doesn't match."}, status=400)

            hand.remove(card)
            discard.append(card)
            current_suit = card_suit(card)

            # Special card effects (Kenyan rules)
            skip = False
            if r == "2":
                draw_stack += 2          # next player draws 2 (stackable)
            elif r == "3":
                draw_stack += 3          # next player draws 3 (stackable)
            elif r == "J":
                skip = True              # skip next player
            elif r == "K":
                direction *= -1          # kickback - reverse direction
            elif r == "Q" or r == "8":
                current_suit = chosen_suit if chosen_suit else card_suit(card)  # question card
            elif r == "A":
                draw_stack = 0           # ace cancels draw penalty
                current_suit = chosen_suit if chosen_suit else card_suit(card)

            # Check win - cannot finish with special card
            # Block winning with special card
            if not hand and is_special(card):
                hand.append(card)  # put it back
                discard.pop()
                ps.hand = hand; ps.save(update_fields=["hand"])
                game.deck = deck; game.discard = discard
                game.save()
                return Response({"error": "Cannot finish with a special card! Play a number card (4-10) last."}, status=400)

            if not hand and not is_special(card):
                # Winner!
                ps.hand = hand; ps.save(update_fields=["hand"])
                game.winner = request.user
                game.status = "finished"
                # Payout
                if game.stake > 0:
                    pot = game.stake * game.players.count()
                    w = get_or_create_wallet(request.user)
                    w.balance += pot
                    w.save(update_fields=["balance"])
                    Transaction.objects.create(wallet=w, amount=pot, tx_type="prediction_win", description=f"Maua win #{game.id}")
                game.deck = deck; game.discard = discard
                game.current_suit = current_suit; game.direction = direction
                game.save()
                return Response(serialize_maua(game, request.user.id))

            # Advance turn
            steps = 2 if skip else 1
            game.current_idx = (game.current_idx + direction * steps) % n
            game.direction = direction
            game.draw_stack = draw_stack
            game.current_suit = current_suit

            ps.hand = hand; ps.save(update_fields=["hand"])
            game.deck = deck; game.discard = discard
            game.save()
            return Response(serialize_maua(game, request.user.id))

        return Response({"error": "Invalid action."}, status=400)
