import discord
from random import shuffle, choices, choice
from discord.ext import commands
from utils.sql_commands import DatabaseManager
from utils.bank_functions import bank_data, update_money
from datetime import datetime
import asyncio
import random

# --- Constants and Configs ---

lottery_list = ["🎁", "🎮", "🎷", "🔫", "📸", "🎃", "🏅"]
weights = [0.15, 0.1, 0.02, 0.2, 0.1, 0.28, 0.2]

lottery_win = {
    "🎁": 2,
    "🎁🎁": 5,
    "🎁🎁🎁": 50,
    "🔫🔫🔫": 3,
    "📸📸📸": 5,
    "🎃🎃🎃": 7,
    "🏅🏅🏅": 10,
    "🎮🎮🎮": 25,
    "🎷🎷🎷": 1000,
}

bj_values = {
    "2♦️": 2,
    "2♥️": 2,
    "2♣️": 2,
    "2♠️": 2,
    "3♦️": 3,
    "3♥️": 3,
    "3♣️": 3,
    "3♠️": 3,
    "4♦️": 4,
    "4♥️": 4,
    "4♣️": 4,
    "4♠️": 4,
    "5♦️": 5,
    "5♥️": 5,
    "5♣️": 5,
    "5♠️": 5,
    "6♦️": 6,
    "6♥️": 6,
    "6♣️": 6,
    "6♠️": 6,
    "7♦️": 7,
    "7♥️": 7,
    "7♣️": 7,
    "7♠️": 7,
    "8♦️": 8,
    "8♥️": 8,
    "8♣️": 8,
    "8♠️": 8,
    "9♦️": 9,
    "9♥️": 9,
    "9♣️": 9,
    "9♠️": 9,
    "10♦️": 10,
    "10♥️": 10,
    "10♣️": 10,
    "10♠️": 10,
    "J♦️": 10,
    "J♥️": 10,
    "J♣️": 10,
    "J♠️": 10,
    "Q♦️": 10,
    "Q♥️": 10,
    "Q♣️": 10,
    "Q♠️": 10,
    "K♦️": 10,
    "K♥️": 10,
    "K♣️": 10,
    "K♠️": 10,
    "A♦️": 11,
    "A♥️": 11,
    "A♣️": 11,
    "A♠️": 11,
}

ROOMS_FILE = "active_rooms.json"

# --- Blackjack Classes and Helpers ---


class Deck:
    def __init__(self):
        self.deck = list(bj_values.keys())
        shuffle(self.deck)

    async def deal(self):
        return self.deck.pop()


class Hand:
    def __init__(self, name:str, bet:int):
        self.cards = []
        self.value = 0
        self.aces = 0
        self.bust = False
        self.name = name
        self.bet = bet

    async def add_card(self, card):
        self.cards.append(card)
        self.aces = ("".join(self.cards)).count("A")
        value = 0
        for card in self.cards:
            value += bj_values[card]
        self.value = value
        while self.value > 21 and self.aces:
            self.value -= 10
            self.aces -= 1


async def hit(deck, hand):
    await hand.add_card(await deck.deal())


async def payout(ctx, play, amount, multiplier, db):
    player_balance = int(
        db.fetch_one("SELECT WALLET FROM economy WHERE ID = %s", (ctx.author.id,)).get(
            "WALLET", 0
        )
    )
    db.execute_query(
        "UPDATE economy SET WALLET = %s WHERE ID = %s",
        (player_balance + amount * (multiplier - 1), ctx.author.id),
    )
    await ctx.reply(
        f"{play}\nBet: {amount:,}\nYou won {amount * multiplier:,} <:flooney:1194943899765051473>"
    )


async def check_winner(msg: discord.Interaction, player_hand, dealer_hand, db):
    p_value, d_value = player_hand.value, dealer_hand.value
    embed = discord.Embed(description="Blackjack")
    embed.add_field(
        name="Bet: ",
        value=f"{player_hand.bet:,}<:flooney:1194943899765051473>",
        inline=True,
    )
    embed.add_field(
        name=player_hand.name,
        value=f"{', '.join(player_hand.cards)}: {player_hand.value}",
        inline=False,
    )
    embed.add_field(
        name=dealer_hand.name,
        value=f"{', '.join(dealer_hand.cards)}: {dealer_hand.value}",
        inline=False,
    )

    # Check for Blackjack
    if p_value == 21 and len(player_hand.cards) == 2:
        if d_value == 21 and len(dealer_hand.cards) == 2:
            embed.add_field(
                name="Winner:", value="Draw (Both got Blackjack!)", inline=False
            )
            player_balance = int(
                db.fetch_one(
                    "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,)
                ).get("WALLET", 0)
            )
            db.execute_query(
                "UPDATE economy SET WALLET = %s WHERE ID = %s",
                (player_balance + player_hand.bet, player_hand.name.id),
            )
        else:
            embed.add_field(
                name="Winner:", value=f"{player_hand.name} (Blackjack!)", inline=False
            )
            player_balance = int(
                db.fetch_one(
                    "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,)
                ).get("WALLET", 0)
            )
            db.execute_query(
                "UPDATE economy SET WALLET = %s WHERE ID = %s",
                (player_balance + int(player_hand.bet * 3), player_hand.name.id),
            )
    elif d_value == 21 and len(dealer_hand.cards) == 2:
        embed.add_field(
            name="Winner:", value=f"{dealer_hand.name} (Blackjack!)", inline=False
        )
    else:
        if (p_value > d_value and not player_hand.bust) or dealer_hand.bust:
            embed.add_field(name="Winner:", value=f"{player_hand.name}", inline=False)
            player_balance = int(
                db.fetch_one(
                    "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,)
                ).get("WALLET", 0)
            )
            db.execute_query(
                "UPDATE economy SET WALLET = %s WHERE ID = %s",
                (player_balance + player_hand.bet * 2, player_hand.name.id),
            )
        elif (p_value < d_value and not dealer_hand.bust) or player_hand.bust:
            embed.add_field(name="Winner:", value=f"{dealer_hand.name}", inline=False)
        else:
            embed.add_field(name="Winner:", value="Draw", inline=False)
            player_balance = int(
                db.fetch_one(
                    "SELECT WALLET FROM economy WHERE ID = %s", (player_hand.name.id,)
                ).get("WALLET", 0)
            )
            db.execute_query(
                "UPDATE economy SET WALLET = %s WHERE ID = %s",
                (player_balance + player_hand.bet, player_hand.name.id),
            )

    await msg.edit_original_response(embed=embed)


async def dealer(msg: discord.Interaction, player_hand, dealer_hand, cards, db):
    while dealer_hand.value < 17 and not player_hand.bust and not dealer_hand.bust:
        await hit(cards, dealer_hand)
        if dealer_hand.value > 21:
            dealer_hand.bust = True
    embed = discord.Embed(description="Blackjack")
    embed.add_field(
        name="Bet: ",
        value=f"{player_hand.bet:,}<:flooney:1194943899765051473>",
        inline=True,
    )
    embed.add_field(
        name=player_hand.name,
        value=f"{', '.join(player_hand.cards)}: {player_hand.value}",
        inline=False,
    )
    embed.add_field(
        name=dealer_hand.name,
        value=f"{', '.join(dealer_hand.cards)}: {dealer_hand.value}",
        inline=False,
    )
    await msg.edit_original_response(embed=embed)
    await check_winner(msg, player_hand, dealer_hand, db)


class MyView(discord.ui.View):
    def __init__(self, player_hand, dealer_hand, cards, db):
        super().__init__()
        self.player_hand = player_hand
        self.dealer_hand = dealer_hand
        self.cards = cards
        self.db = db

    @discord.ui.button(label="Hit", style=discord.ButtonStyle.primary, emoji="✅")
    async def hit(self, interaction: discord.Interaction, button: discord.ui.Button):
        if interaction.user.id == self.player_hand.name.id:
            await hit(self.cards, self.player_hand)
            embed = discord.Embed(description="Blackjack")
            embed.add_field(
                name="Bet: ",
                value=f"{self.player_hand.bet:,}<:flooney:1194943899765051473>",
                inline=True,
            )
            embed.add_field(
                name=self.player_hand.name,
                value=f"{', '.join(self.player_hand.cards)}: {self.player_hand.value}",
                inline=False,
            )
            embed.add_field(
                name=self.dealer_hand.name,
                value=f"{(self.dealer_hand.cards)[0]}",
                inline=False,
            )
            msg = interaction
            if self.player_hand.value < 21:
                await msg.response.edit_message(embed=embed)
            else:
                await msg.response.edit_message(embed=embed, view=None)
                if self.player_hand.value > 21:
                    self.player_hand.bust = True
                await dealer(
                    msg, self.player_hand, self.dealer_hand, self.cards, self.db
                )

    @discord.ui.button(label="Stand", style=discord.ButtonStyle.secondary, emoji="🛑")
    async def stand(self, interaction: discord.Interaction, button: discord.ui.Button):
        if interaction.user.id == self.player_hand.name.id:
            embed = discord.Embed(description="Blackjack")
            embed.add_field(
                name="Bet: ",
                value=f"{self.player_hand.bet:,}<:flooney:1194943899765051473>",
                inline=True,
            )
            embed.add_field(
                name=self.player_hand.name,
                value=f"{', '.join(self.player_hand.cards)}: {self.player_hand.value}",
                inline=False,
            )
            embed.add_field(
                name=self.dealer_hand.name,
                value=f"{(self.dealer_hand.cards)[0]}",
                inline=False,
            )
            msg = interaction
            await msg.response.edit_message(embed=embed, view=None)
            await dealer(msg, self.player_hand, self.dealer_hand, self.cards, self.db)


# --- Main Gamble Cog ---


class Gamble(commands.Cog):
    """Cog for all gambling-related commands."""

    def __init__(self, client):
        self.client = client
        self.db = DatabaseManager()
        self.active_rooms = {}
        self.room_timers = {}

    def save_rooms(self):
        """Save all active rooms to the database."""
        try:
            for channel_id, room in self.active_rooms.items():
                invited_str = ",".join(str(uid) for uid in room["invited"])
                self.db.execute_query(
                    "REPLACE INTO gamble_rooms (channel_id, host_id, invited, inactivity) VALUES (%s, %s, %s, %s)",
                    (channel_id, room["host"], invited_str, room["inactivity"]),
                )
        except Exception as e:
            print(f"[DB ERROR] Failed to save rooms: {e}")

    def load_rooms(self):
        """Load all rooms from the database."""
        try:
            self.active_rooms = {}
            rows = self.db.fetch_all("SELECT * FROM gamble_rooms")
            for row in rows:
                invited = set(int(uid) for uid in row["invited"].split(",") if uid)
                self.active_rooms[int(row["channel_id"])] = {
                    "host": int(row["host_id"]),
                    "invited": invited,
                    "inactivity": int(row["inactivity"]),
                }
        except Exception as e:
            print(f"[DB ERROR] Failed to load rooms: {e}")

    def delete_room(self, channel_id):
        """Delete a room from the database and memory."""
        try:
            self.db.execute_query(
                "DELETE FROM gamble_rooms WHERE channel_id = %s", (channel_id,)
            )
        except Exception as e:
            print(f"[DB ERROR] Failed to delete room: {e}")
        self.active_rooms.pop(channel_id, None)
        self.save_rooms()

    async def cog_load(self):
        """Load rooms on cog load."""
        self.load_rooms()

    async def cog_unload(self):
        """Save rooms on cog unload."""
        self.save_rooms()

    async def _check_limit_and_exclusion(self, ctx, amount: int) -> bool:
        """Returns True if user is allowed to gamble, else sends a message and returns False."""
        user = ctx.author
        if amount <= 0:
            await ctx.reply("⚠️ Bet amount must be positive.", mention_author=False)
            return False
        try:
            limit_row = self.db.fetch_one(
                "SELECT * FROM gamble_limits WHERE USERID = %s", (user.id,)
            )
        except Exception as e:
            await ctx.reply(
                "⚠️ Database error. Please try again later.", mention_author=False
            )
            print(f"[DB ERROR] Limit check: {e}")
            return False
        if limit_row:
            if (
                limit_row.get("EXCLUDED_UNTIL")
                and datetime.utcnow() < limit_row["EXCLUDED_UNTIL"]
            ):
                await ctx.reply(
                    "🚫 You are currently self-excluded from gambling.",
                    mention_author=False,
                )
                return False
            if limit_row.get("DAILY_LIMIT") and amount > limit_row["DAILY_LIMIT"]:
                await ctx.reply(
                    f"🚫 Your personal bet limit is {limit_row['DAILY_LIMIT']:,}.",
                    mention_author=False,
                )
                return False
        return True

    async def _send_embed(
        self, ctx, description: str, color: discord.Color = discord.Color.blurple()
    ):
        """Helper to send an embed message."""
        embed = discord.Embed(description=description, color=color)
        await ctx.reply(embed=embed, mention_author=False)

    # --- Coinflip ---
    @commands.cooldown(1, 2, commands.BucketType.user)
    @commands.command(
        name="coinflip",
        aliases=["cf", "coin_flip"],
        usage="<bet_on: heads(H) or tails(T)> <amount: integer>",
        description="Flip a coin and bet on heads or tails.",
        brief="Bet on heads or tails.",
    )
    @commands.guild_only()
    async def coin_flip(self, ctx, bet_on: str, amount: int):
        if not await self._check_limit_and_exclusion(ctx, amount):
            return
        user = ctx.author
        bet_on = "heads" if "h" in bet_on.lower() else "tails"
        reward = round(amount / 2)
        users = await bank_data(user)
        if int(users["WALLET"]) < amount:
            return await self._send_embed(
                ctx, "❌ You don't have enough money.", discord.Color.red()
            )
        coin = ["heads", "tails"]
        result = choice(coin)
        if result != bet_on:
            await update_money(user, wallet=-abs(amount))
            return await self._send_embed(
                ctx,
                f"🪙 Got **{result}**, you lost **{amount:,}**.",
                discord.Color.red(),
            )
        await update_money(user, wallet=+abs(amount))
        return await self._send_embed(
            ctx,
            f"🪙 Got **{result}**, you won **{amount + reward:,}**!",
            discord.Color.green(),
        )

    # --- Dice ---
    @commands.cooldown(1, 2, commands.BucketType.user)
    @commands.command(
        name="dice",
        usage="<amount: integer> <bet_on: integer (1-6)>",
        description="Bet on a dice roll (choose a number 1-6).",
        brief="Bet on a dice roll.",
    )
    async def dice(self, ctx, amount: int, bet_on: int = 6):
        if not await self._check_limit_and_exclusion(ctx, amount):
            return
        user = ctx.author
        rdice = [1, 2, 3, 4, 5, 6]
        if bet_on not in rdice:
            return await self._send_embed(
                ctx, "🎲 Enter a number of dice (1 - 6).", discord.Color.orange()
            )
        users = await bank_data(user)
        if int(users["WALLET"]) < amount:
            return await self._send_embed(
                ctx, "❌ You don't have enough money.", discord.Color.red()
            )
        rand_num = choice(rdice)
        if rand_num != bet_on:
            await update_money(user, wallet=-abs(amount))
            return await self._send_embed(
                ctx,
                f"🎲 Got **{rand_num}**, you lost **{amount:,}**.",
                discord.Color.red(),
            )
        reward = round(amount / 2)
        await update_money(user, wallet=+abs(reward))
        await self._send_embed(
            ctx,
            f"🎲 Got **{rand_num}**, you won **{amount + reward:,}**!",
            discord.Color.green(),
        )

    # --- Slots ---
    @commands.cooldown(1, 1, commands.BucketType.user)
    @commands.command(
        name="slots",
        usage="<bet: integer or 'all'>",
        description="Spin the slot machine for a chance to win big!",
        brief="Spin the slot machine.",
    )
    async def slots(self, ctx, bet):
        user = ctx.author
        player_balance = int(
            self.db.fetch_one(
                "SELECT WALLET FROM economy WHERE ID = %s", (user.id,)
            ).get("WALLET", 0)
        )
        bet = int(bet.replace("all", str(player_balance)))
        if not await self._check_limit_and_exclusion(ctx, bet):
            return
        if player_balance < bet:
            await self._send_embed(
                ctx, "❌ You have insufficient funds.", discord.Color.red()
            )
            return

        # Animation: edit the embed to show spinning, then result
        spin_emoji = "<a:slots:1381060458999713843>"
        embed = discord.Embed(
            description=f"{spin_emoji} {spin_emoji} {spin_emoji}\nSpinning...",
            color=discord.Color.blurple(),
        )
        msg = await ctx.send(embed=embed)
        await asyncio.sleep(1.2)

        play = "".join(choices(lottery_list, weights=weights, k=3))
        result_embed = discord.Embed(
            description=f"{play[0]} {play[1]} {play[2]}",
            color=(
                discord.Color.green()
                if play in lottery_win or play.count("🎁") > 0
                else discord.Color.red()
            ),
        )
        # Ensure description is always a string
        if result_embed.description is None:
            result_embed.description = ""

        if play in lottery_win:
            mult = lottery_win[play]
            result_embed.description = (result_embed.description or "") + (
                f"\nYou won **{bet * mult:,}** <:flooney:1194943899765051473>"
            )
            await update_money(user, wallet=bet * (mult - 1))
        elif play.count("🎁") == 2:
            mult = lottery_win["🎁🎁"]
            result_embed.description = (result_embed.description or "") + (
                f"\nYou won **{bet * mult:,}** <:flooney:1194943899765051473>"
            )
            await update_money(user, wallet=bet * (mult - 1))
        elif play.count("🎁") == 1:
            mult = lottery_win["🎁"]
            result_embed.description = (result_embed.description or "") + (
                f"\nYou won **{bet * mult:,}** <:flooney:1194943899765051473>"
            )
            await update_money(user, wallet=bet * (mult - 1))
        else:
            await update_money(user, wallet=-bet)
            result_embed.description = (result_embed.description or "") + (
                f"\nYou lost **{int(bet):,}** <:flooney:1194943899765051473>"
            )

        await msg.edit(embed=result_embed)

    # --- Roulette ---
    @commands.cooldown(1, 2, commands.BucketType.user)
    @commands.command(
        name="roulette",
        usage="<bet_type> <amount: integer>",
        description=(
            "Bet on roulette! Options: "
            "`red`, `black`, `green`, `even`, `odd`, "
            "`1st12`, `2nd12`, `3rd12` (dozens), "
            "`col1`, `col2`, `col3` (columns), or a number (0-36)."
        ),
        brief="Bet on roulette.",
    )
    async def roulette(self, ctx, bet_type: str, amount: int):
        if not await self._check_limit_and_exclusion(ctx, amount):
            return
        user = ctx.author
        colors = {
            "red": [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36],
            "black": [
                2,
                4,
                6,
                8,
                10,
                11,
                13,
                15,
                17,
                20,
                22,
                24,
                26,
                28,
                29,
                31,
                33,
                35,
            ],
            "green": [0],
        }
        bet_type = bet_type.lower()
        valid_bets = [
            "red",
            "black",
            "green",
            "even",
            "odd",
            "1st12",
            "2nd12",
            "3rd12",
            "col1",
            "col2",
            "col3",
        ] + [str(i) for i in range(37)]
        if bet_type not in valid_bets:
            return await self._send_embed(
                ctx,
                "Bet on 'red', 'black', 'green', 'even', 'odd', '1st12', '2nd12', '3rd12', 'col1', 'col2', 'col3', or a number (0-36).",
                discord.Color.orange(),
            )
        result = choice(range(37))
        color = (
            "green" if result == 0 else "red" if result in colors["red"] else "black"
        )
        payout = 0
        # Number bet
        if bet_type.isdigit() and int(bet_type) == result:
            payout = amount * 35
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        # Color bet
        elif bet_type == color:
            payout = amount * (14 if color == "green" else 2)
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        # Even/Odd
        elif bet_type == "even" and result != 0 and result % 2 == 0:
            payout = amount * 2
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        elif bet_type == "odd" and result % 2 == 1:
            payout = amount * 2
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        # Dozens
        elif bet_type == "1st12" and 1 <= result <= 12:
            payout = amount * 3
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        elif bet_type == "2nd12" and 13 <= result <= 24:
            payout = amount * 3
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        elif bet_type == "3rd12" and 25 <= result <= 36:
            payout = amount * 3
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        # Columns
        elif bet_type == "col1" and result in [i for i in range(1, 37, 3)]:
            payout = amount * 3
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        elif bet_type == "col2" and result in [i for i in range(2, 37, 3)]:
            payout = amount * 3
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        elif bet_type == "col3" and result in [i for i in range(3, 37, 3)]:
            payout = amount * 3
            msg = f"🎲 The ball landed on {result} ({color}). You won {payout:,}!"
        else:
            payout = -amount
            msg = f"🎲 The ball landed on {result} ({color}). You lost {amount:,}."
        await update_money(user, wallet=payout)
        await self._send_embed(
            ctx, msg, discord.Color.green() if payout > 0 else discord.Color.red()
        )

    # --- Poker ---
    @commands.cooldown(1, 2, commands.BucketType.user)
    @commands.is_owner()
    @commands.command(
        name="poker",
        usage="<bet: integer>",
        description="Play a simple 5-card draw poker game against the bot.",
        brief="Play poker against the bot.",
    )
    async def poker(self, ctx, bet: int):
        if not await self._check_limit_and_exclusion(ctx, bet):
            return
        user = ctx.author
        user_data = await bank_data(user)
        if int(user_data["WALLET"]) < bet:
            await self._send_embed(
                ctx, "❌ You don't have enough money.", discord.Color.red()
            )
            return
        # Poker deck and hands
        suits = ["♠️", "♥️", "♦️", "♣️"]
        ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]
        deck = [f"{r}{s}" for r in ranks for s in suits]
        random.shuffle(deck)
        player_hand = [deck.pop() for _ in range(5)]
        bot_hand = [deck.pop() for _ in range(5)]

        # Simple hand value: count pairs, triples, etc. (not full poker logic)
        def hand_value(hand):
            values = [card[:-2] if card[:-2] != "10" else "10" for card in hand]
            counts = {v: values.count(v) for v in set(values)}
            if 4 in counts.values():
                return 7  # Four of a kind
            if sorted(counts.values()) == [2, 3]:
                return 6  # Full house
            if 3 in counts.values():
                return 3  # Three of a kind
            if list(counts.values()).count(2) == 2:
                return 2  # Two pair
            if 2 in counts.values():
                return 1  # One pair
            return 0  # High card

        player_score = hand_value(player_hand)
        bot_score = hand_value(bot_hand)

        if player_score > bot_score:
            await update_money(user, wallet=bet)
            msg = f"🃏 Your hand: {', '.join(player_hand)}\n🤖 Bot's hand: {', '.join(bot_hand)}\nYou win {bet:,} coins!"
        elif player_score < bot_score:
            await update_money(user, wallet=-bet)
            msg = f"🃏 Your hand: {', '.join(player_hand)}\n🤖 Bot's hand: {', '.join(bot_hand)}\nYou lose {bet:,} coins!"
        else:
            msg = f"🃏 Your hand: {', '.join(player_hand)}\n🤖 Bot's hand: {', '.join(bot_hand)}\nIt's a draw! No coins won or lost."
        await self._send_embed(
            ctx,
            msg,
            (
                discord.Color.green()
                if "win" in msg
                else discord.Color.red() if "lose" in msg else discord.Color.blurple()
            ),
        )

    # --- Higher/Lower ---
    @commands.cooldown(1, 2, commands.BucketType.user)
    @commands.command(
        name="higherlower",
        usage="<bet: integer>",
        description="Guess if the next number will be higher or lower.",
        brief="Guess higher or lower.",
    )
    async def higherlower(self, ctx, bet: int):
        if not await self._check_limit_and_exclusion(ctx, bet):
            return
        user = ctx.author
        number = random.randint(1, 100)
        await self._send_embed(
            ctx,
            f"The number is **{number}**. Will the next number be higher or lower? Type `higher` or `lower`.",
            discord.Color.blurple(),
        )

        def check(m):
            return (
                m.author == ctx.author
                and m.channel == ctx.channel
                and m.content.lower() in ["higher", "lower"]
            )

        try:
            guess_msg = await self.client.wait_for("message", check=check, timeout=15)
        except Exception:
            await self._send_embed(
                ctx, "Timed out. Please try again.", discord.Color.orange()
            )
            return
        next_number = random.randint(1, 100)
        win = (guess_msg.content.lower() == "higher" and next_number > number) or (
            guess_msg.content.lower() == "lower" and next_number < number
        )
        if next_number == number:
            msg = f"The next number was also **{next_number}**. It's a tie! No coins won or lost."
        elif win:
            await update_money(user, wallet=bet)
            msg = f"The next number was **{next_number}**. You win {bet:,} coins!"
        else:
            await update_money(user, wallet=-bet)
            msg = f"The next number was **{next_number}**. You lose {bet:,} coins!"
        await self._send_embed(
            ctx, msg, discord.Color.green() if win else discord.Color.red()
        )

    # --- Scratch ---
    @commands.cooldown(1, 2, commands.BucketType.user)
    @commands.command(
        name="scratch",
        usage="<bet: integer>",
        description="Buy a scratch card for a chance to win up to 10x your bet.",
        brief="Buy a scratch card.",
    )
    async def scratch(self, ctx, bet: int):
        if not await self._check_limit_and_exclusion(ctx, bet):
            return
        user = ctx.author
        symbols = ["🍀", "💎", "⭐", "🍒", "7️⃣"]
        card = [random.choice(symbols) for _ in range(3)]
        payout = 0
        if card.count(card[0]) == 3:
            payout = bet * 10
            msg = f"Scratch Card: {' '.join(card)}\nJackpot! You win {payout:,} coins!"
        elif len(set(card)) == 2:
            payout = bet * 2
            msg = f"Scratch Card: {' '.join(card)}\nTwo of a kind! You win {payout:,} coins!"
        else:
            payout = -bet
            msg = f"Scratch Card: {' '.join(card)}\nNo match. You lose {bet:,} coins."
        await update_money(user, wallet=payout)
        await self._send_embed(
            ctx, msg, discord.Color.green() if payout > 0 else discord.Color.red()
        )

    # --- All other user-facing responses (invite, kick, end_gameroom, etc.) ---
    # Replace ctx.reply(...) or ctx.send(...) with self._send_embed(...) for consistency.

    @commands.command(
        name="gameroom",
        usage="[game: str] [inactivity: int (minutes)]",
        description="Create a private game room that auto-deletes after inactivity.",
        brief="Create a private game room.",
    )
    @commands.guild_only()
    async def gameroom(self, ctx, game: str = "game", inactivity: int = 10):
        """Create a private text channel for a game session."""
        guild = ctx.guild
        overwrites = {
            guild.default_role: discord.PermissionOverwrite(read_messages=False),
            ctx.author: discord.PermissionOverwrite(
                read_messages=True, send_messages=True
            ),
        }
        category = ctx.channel.category
        channel_name = f"{game}-room-{ctx.author.display_name}".replace(
            " ", "-"
        ).lower()
        channel = await guild.create_text_channel(
            name=channel_name,
            overwrites=overwrites,
            category=category,
            reason="Private game room",
        )
        await channel.send(
            f"{ctx.author.mention} Your private **{game}** session has started!\n"
            f"Use `invite <user>` and `kick <user>` in this channel to manage access.\n"
            f"This channel will be deleted after **{inactivity} minutes of inactivity**."
        )

        self.active_rooms[channel.id] = {
            "host": ctx.author.id,
            "invited": set([ctx.author.id]),
            "inactivity": inactivity,
        }
        self.save_rooms()
        self.reset_inactivity_timer(channel.id, inactivity)

    def reset_inactivity_timer(self, channel_id, inactivity):
        """Reset the inactivity timer for a game room."""
        if channel_id in self.room_timers:
            self.room_timers[channel_id].cancel()
        task = asyncio.create_task(self.inactivity_task(channel_id, inactivity))
        self.room_timers[channel_id] = task

    async def inactivity_task(self, channel_id, inactivity):
        """Delete the channel after inactivity period."""
        try:
            await asyncio.sleep(inactivity * 60)
            channel = self.client.get_channel(channel_id)
            if channel:
                await channel.send("Room deleted due to inactivity.")
                await channel.delete(reason="Game room inactive")
            self.delete_room(channel_id)
            self.room_timers.pop(channel_id, None)
        except asyncio.CancelledError:
            pass

    @commands.Cog.listener()
    async def on_message(self, message):
        """Reset inactivity timer on message in a game room."""
        if (
            hasattr(self, "active_rooms")
            and message.channel.id in self.active_rooms
            and not message.author.bot
        ):
            inactivity = self.active_rooms[message.channel.id].get("inactivity", 10)
            self.reset_inactivity_timer(message.channel.id, inactivity)

    @commands.command(
        name="invite",
        usage="<user: id|name>",
        description="Invite a user to your private game room.",
        brief="Invite a user to your game room.",
        hidden=True,
    )
    async def invite(
        self,
        ctx,
        member: discord.Member | None = None,
        *,
        identifier: str | None = None,
    ):
        """Invite a user to the game room by mention, ID, or username."""
        room = self.active_rooms.get(ctx.channel.id)
        if not room or ctx.author.id != room["host"]:
            await ctx.reply(
                "Only the host can invite others, and only in a private game room channel.",
                mention_author=False,
            )
            return

        # If member is not provided, try to resolve by ID or name
        if member is None and identifier:
            try:
                member = ctx.guild.get_member(int(identifier))
            except ValueError:
                member = discord.utils.find(
                    lambda m: m.name.lower() == identifier.lower()
                    or m.display_name.lower() == identifier.lower(),
                    ctx.guild.members,
                )
        if member is None:
            await ctx.reply(
                "User not found. Please use a mention, user ID, or username.",
                mention_author=False,
            )
            return

        await ctx.channel.set_permissions(
            member, read_messages=True, send_messages=True
        )
        room["invited"].add(member.id)
        self.save_rooms()
        await ctx.send(
            f"{member.mention} has been invited to the game room!",
            allowed_mentions=discord.AllowedMentions(users=True),
        )

    @commands.command(
        name="kick",
        usage="<user: mention>",
        description="Kick a user from your private game room.",
        brief="Kick a user from your game room.",
        hidden=True,
    )
    async def kick(self, ctx, member: discord.Member):
        """Kick a user from the game room."""
        room = self.active_rooms.get(ctx.channel.id)
        if not room or ctx.author.id != room["host"]:
            await ctx.reply(
                "Only the host can kick others, and only in a private game room channel.",
                mention_author=False,
            )
            return
        if member.id == ctx.author.id:
            await ctx.reply("You cannot kick yourself (host).", mention_author=False)
            return
        if member.id not in room["invited"]:
            await ctx.reply(
                f"{member.mention} is not in this game room.", mention_author=False
            )
            return
        await ctx.channel.set_permissions(member, overwrite=None)
        room["invited"].discard(member.id)
        self.save_rooms()
        await ctx.reply(
            f"{member.mention} has been kicked from the game room!",
            mention_author=False,
        )

    @commands.command(
        name="end",
        aliases=["delete"],
        description="End and delete your private game room.",
        brief="Delete your game room.",
        hidden=True,
    )
    async def end_gameroom(self, ctx):
        """Allows the host to manually delete the game room channel."""
        room = self.active_rooms.get(ctx.channel.id)
        if not room or ctx.author.id != room["host"]:
            await ctx.reply(
                "Only the host can end this game room, and only in a private game room channel.",
                mention_author=False,
            )
            return
        await ctx.send("This game room will now be deleted by the host.")
        self.delete_room(ctx.channel.id)
        await ctx.channel.delete(reason="Game room ended by host")


async def setup(client):
    await client.add_cog(Gamble(client))
