import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from django.utils import timezone


WINNING_COMBOS = [
    [0,1,2],[3,4,5],[6,7,8],  # rows
    [0,3,6],[1,4,7],[2,5,8],  # cols
    [0,4,8],[2,4,6],          # diags
]


def check_winner(board):
    for combo in WINNING_COMBOS:
        a,b,c = combo
        if board[a] and board[a] == board[b] == board[c]:
            return board[a]
    if all(board):
        return "D"
    return None


class TicTacToeConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope["user"]
        if not self.user or not self.user.is_authenticated:
            await self.close(); return
        self.game_id = self.scope["url_route"]["kwargs"]["game_id"]
        self.group   = f"ttt_{self.game_id}"
        await self.channel_layer.group_add(self.group, self.channel_name)
        await self.accept()
        # Send current game state
        game = await self.get_game()
        if game:
            await self.send_state(game)

    async def disconnect(self, code):
        if hasattr(self, "group"):
            await self.channel_layer.group_discard(self.group, self.channel_name)

    async def receive(self, text_data):
        data = json.loads(text_data)
        msg_type = data.get("type")

        if msg_type == "move":
            await self.handle_move(data.get("index"))
        elif msg_type == "join":
            await self.handle_join()

    async def handle_join(self):
        game = await self.join_game()
        if game:
            await self.channel_layer.group_send(self.group, {
                "type": "game.update",
                "game": await self.serialize(game),
            })

    async def handle_move(self, index):
        if index is None: return
        result = await self.make_move(index)
        if result:
            await self.channel_layer.group_send(self.group, {
                "type": "game.update",
                "game": result,
            })

    async def game_update(self, event):
        await self.send(text_data=json.dumps({
            "type": "game_update",
            "game": event["game"],
        }))

    @database_sync_to_async
    def get_game(self):
        from .models import TicTacToe
        return TicTacToe.objects.filter(id=self.game_id).first()

    @database_sync_to_async
    def join_game(self):
        from .models import TicTacToe
        from wallet.utils import get_or_create_wallet
        from wallet.models import Transaction
        game = TicTacToe.objects.filter(id=self.game_id, status="waiting").first()
        if not game or game.player_x == self.user:
            return None
        wallet = get_or_create_wallet(self.user)
        if wallet.balance < game.stake:
            return None
        wallet.balance -= game.stake
        wallet.save(update_fields=["balance"])
        Transaction.objects.create(wallet=wallet, amount=-game.stake, tx_type="prediction_stake", description=f"TTT game #{game.id}")
        game.player_o = self.user
        game.status = "playing"
        game.board = [""] * 9
        game.save()
        return game

    @database_sync_to_async
    def make_move(self, index):
        from .models import TicTacToe
        from wallet.utils import get_or_create_wallet
        from wallet.models import Transaction
        from notifications.utils import create_notification
        game = TicTacToe.objects.filter(id=self.game_id, status="playing").first()
        if not game: return None
        is_x = game.player_x == self.user
        is_o = game.player_o == self.user
        if not (is_x or is_o): return None
        my_mark = "X" if is_x else "O"
        if game.current != my_mark: return None
        if game.board[index]: return None

        board = list(game.board)
        board[index] = my_mark
        game.board = board
        winner = check_winner(board)

        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}")
                create_notification(win_user, "ttt_win", f"You won {pot} pts in Tic Tac Toe! 🎉", link="/user/arena")
                create_notification(lose_user, "ttt_lose", f"You lost {game.stake} pts in Tic Tac Toe.", link="/user/arena")
        else:
            game.current = "O" if my_mark == "X" else "X"

        game.save()
        from asgiref.sync import sync_to_async
        return self.serialize_sync(game)

    def serialize_sync(self, game):
        return {
            "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,
        }

    @database_sync_to_async
    def serialize(self, game):
        return self.serialize_sync(game)
