import discord
from discord.ext import commands
import random
import asyncio
from collections import Counter

ROLES = ["Werewolf", "Seer", "Doctor", "Villager"]

ROLE_DESCRIPTIONS = {
    "Werewolf": "You are a **Werewolf**! Work with your fellow werewolves at night to eliminate villagers. Your goal is to outnumber the villagers.",
    "Seer": "You are the **Seer**! Each night, you may select a player to learn their true role.",
    "Doctor": "You are the **Doctor**! Each night, you may choose a player to protect from elimination.",
    "Villager": "You are a **Villager**! Try to find and vote out the werewolves during the day.",
}


class WerewolvesGame:
    def __init__(self, channel):
        self.channel = channel
        self.players = []
        self.started = False
        self.roles = {}  # user_id: role
        self.phase = "lobby"  # or "night", "day"
        self.role_threads = {}  # role: thread
        self.votes = {}
        self.vote_targets = []
        self.alive = set()

    def add_player(self, user):
        if user not in self.players:
            self.players.append(user)

    def assign_roles(self):
        # For 6+ players: 2 werewolves, 1 seer, 1 doctor, rest villagers
        n = len(self.players)
        roles_list = ["Werewolf"] * 2 + ["Seer", "Doctor"]
        roles_list += ["Villager"] * (n - len(roles_list))
        random.shuffle(roles_list)
        self.roles = {player.id: role for player, role in zip(self.players, roles_list)}

    def get_players_by_role(self, role):
        return [p for p in self.players if self.roles.get(p.id) == role]

    def reset_votes(self):
        self.votes = {}
        self.vote_targets = []

    def vote(self, voter, target):
        self.votes[voter.id] = target.id

    def tally_votes(self):
        if not self.votes:
            return None
        count = Counter(self.votes.values())
        most_common = count.most_common(1)
        if most_common:
            return most_common[0][0]  # user_id of voted out
        return None


class Games(commands.Cog):
    def __init__(self, client):
        self.client = client
        self.active_games = {}  # channel_id: WerewolvesGame

    @commands.command(name="joinwerewolves", hidden=True)
    async def join_werewolves(self, ctx):
        game = self.active_games.get(ctx.channel.id)
        if not game:
            game = WerewolvesGame(ctx.channel)
            self.active_games[ctx.channel.id] = game
        if game.started:
            await ctx.reply("Game already started!")
            return
        game.add_player(ctx.author)
        await ctx.reply(f"{ctx.author.display_name} joined the game!")

    @commands.command(name="startwerewolves", hidden=True)
    async def start_werewolves(self, ctx):
        game = self.active_games.get(ctx.channel.id)
        if not game or len(game.players) < 2:
            await ctx.reply("Need at least 6 players to start!")
            return
        if game.started:
            await ctx.reply("Game already started!")
            return
        game.started = True
        game.assign_roles()

        # Create threads for roles
        await self.create_role_threads(game)

        # Announce roles in threads (description only once per thread)
        # Werewolf thread: mention all werewolves, send description once
        werewolves = game.get_players_by_role("Werewolf")
        if werewolves:
            thread = game.role_threads["Werewolf"]
            mentions = " ".join([p.mention for p in werewolves])
            desc = ROLE_DESCRIPTIONS["Werewolf"]
            await thread.send(f"{mentions}\nYou are **Werewolves**!\n\n{desc}")

        # Seer thread
        seer = game.get_players_by_role("Seer")
        if seer:
            thread = game.role_threads["Seer"]
            desc = ROLE_DESCRIPTIONS["Seer"]
            await thread.send(f"{seer[0].mention}\nYou are the **Seer**!\n\n{desc}")

        # Doctor thread
        doctor = game.get_players_by_role("Doctor")
        if doctor:
            thread = game.role_threads["Doctor"]
            desc = ROLE_DESCRIPTIONS["Doctor"]
            await thread.send(f"{doctor[0].mention}\nYou are the **Doctor**!\n\n{desc}")

        await ctx.send(
            "Game started! Roles have been assigned in private threads.\nThe game cycle will begin in 30 seconds..."
        )

        # Start the game cycle after 30 seconds
        await asyncio.sleep(30)
        await self.start_cycle(ctx)

    async def create_role_threads(self, game: WerewolvesGame):
        # Werewolves share a thread, others get solo threads
        channel = game.channel
        werewolves = game.get_players_by_role("Werewolf")
        seer = game.get_players_by_role("Seer")
        doctor = game.get_players_by_role("Doctor")

        # Create werewolf group thread
        if werewolves:
            thread = await channel.create_thread(
                name="Werewolves Actions",
                type=discord.ChannelType.private_thread,
                auto_archive_duration=60,
                reason="Werewolves game: Werewolves thread",
            )
            for member in werewolves:
                await thread.add_user(member)
            game.role_threads["Werewolf"] = thread

        # Create seer thread
        if seer:
            thread = await channel.create_thread(
                name="Seer Actions",
                type=discord.ChannelType.private_thread,
                auto_archive_duration=60,
                reason="Werewolves game: Seer thread",
            )
            await thread.add_user(seer[0])
            game.role_threads["Seer"] = thread

        # Create doctor thread
        if doctor:
            thread = await channel.create_thread(
                name="Doctor Actions",
                type=discord.ChannelType.private_thread,
                auto_archive_duration=60,
                reason="Werewolves game: Doctor thread",
            )
            await thread.add_user(doctor[0])
            game.role_threads["Doctor"] = thread

    @commands.command(name="playwerewolves")
    @commands.guild_only()
    async def play_werewolves(self, ctx):
        """Create a private game room for Werewolves and start the game setup."""
        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"werewolves-room-{ctx.author.display_name}".replace(
            " ", "-"
        ).lower()
        channel = await guild.create_text_channel(
            name=channel_name,
            overwrites=overwrites,
            category=category,
            reason="Private Werewolves game room",
        )
        await channel.send(
            f"{ctx.author.mention} Your private **Werewolves** game room has been created!\n"
            f"Use `py addwerewolves <username>` in this channel to add others.\n"
            f"Use `py joinwerewolves` in this channel to join the game.\n"
            f"Once enough players have joined, use `py startwerewolves` to start the game."
        )
        # Optionally, auto-join the creator to the game
        game = WerewolvesGame(channel)
        game.add_player(ctx.author)
        self.active_games[channel.id] = game

    @commands.command(name="addwerewolves", hidden=True)
    @commands.guild_only()
    async def add_werewolves(
        self,
        ctx,
        member: discord.Member | None = None,
        *,
        identifier: str | None = None,
    ):
        """Add a user to the current Werewolves game room by username (case-insensitive, not mention)."""
        game = self.active_games.get(ctx.channel.id)
        if not game:
            await ctx.reply("This is not a Werewolves game room.")
            return

        # Only allow the creator (first player) to add others
        if ctx.author != game.players[0]:
            await ctx.reply("Only the game creator can add players to the room.")
            return

        # Find member by username (case-insensitive)
        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 not member:
            await ctx.reply(f"User '{member}' not found in this server.")
            return

        await ctx.channel.set_permissions(
            member,
            read_messages=True,
            send_messages=True,
        )
        await ctx.send(
            f"{member.mention} has been added to the game room! They can now join the game with `py joinwerewolves`."
        )

    @commands.command(name="startcycle", hidden=True)
    async def start_cycle(self, ctx):
        """Start the Werewolves game cycle (night/day loop)."""
        game = self.active_games.get(ctx.channel.id)
        if not game or not game.started:
            await ctx.send("No active game to start the cycle.")
            return

        await ctx.send("The game cycle is starting!")
        game.phase = "night"
        game.alive = set(game.players)
        game.reset_votes()

        while True:
            # NIGHT PHASE
            await ctx.send(
                "🌙 **Night falls!** Werewolves, Seer, and Doctor, check your threads."
            )
            await asyncio.sleep(10)  # Replace with your night action logic and timing

            # DAY PHASE
            await ctx.send(
                "☀️ **Day breaks!** Discuss and vote to eliminate a player. Use `py vote <username>`."
            )
            game.reset_votes()
            await self.day_voting(ctx, game)
            voted_out_id = game.tally_votes()
            if voted_out_id:
                voted_out = discord.utils.get(ctx.guild.members, id=voted_out_id)
                if voted_out in game.alive:
                    game.alive.remove(voted_out)
                    await ctx.send(f"{voted_out.mention} has been eliminated!")
            else:
                await ctx.send("No one was eliminated today.")

            # Check win condition (example: only werewolves or villagers left)
            werewolves = [p for p in game.alive if game.roles.get(p.id) == "Werewolf"]
            villagers = [p for p in game.alive if game.roles.get(p.id) != "Werewolf"]
            if not werewolves:
                await ctx.send("Villagers win! 🎉")
                break
            if len(werewolves) >= len(villagers):
                await ctx.send("Werewolves win! 🐺")
                break

            await asyncio.sleep(5)  # Short pause before next night

    @commands.command(name="vote", hidden=True)
    async def vote(
        self,
        ctx,
        member: discord.Member | None = None,
        *,
        identifier: str | None = None,
    ):
        """Vote to eliminate a player during the day phase."""
        game = self.active_games.get(ctx.channel.id)
        if not game or ctx.author not in game.alive:
            await ctx.reply("You are not in the game or not alive.")
            return

        # Find member by username (case-insensitive)
        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 not member:
            await ctx.reply(f"User '{member}' not found in this server.")
            return

        if not member or member not in game.alive:
            await ctx.reply("That player is not alive or not found.")
            return
        game.vote(ctx.author, member)
        await ctx.send(
            f"{ctx.author.display_name} voted to eliminate {member.display_name}."
        )

    async def day_voting(self, ctx, game, timeout=30):
        """Wait for votes during the day phase."""
        await ctx.send(f"You have {timeout} seconds to vote.")
        await asyncio.sleep(timeout)


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