import asyncio
import discord

from typing import Optional, Literal
from redbot.core import Config, checks, commands
from redbot.core.utils.chat_formatting import *
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS

from .discord_thread_feature import create_thread, add_user_thread, get_active_threads


class ThreadManager(commands.Cog):
    """
    Better Thread Manager
    """

    def __init__(self, bot):
        self.bot = bot
        self.config = Config.get_conf(self, identifier=165164165133023130, force_registration=True)

        # allowed roles maps role id (str) -> number of threads each user can create with this role (int)
        # threads maps member id str -> list of active thread ids (int) that the user created in the channel
        default_channel = {"allowed_roles": {}, "threads": {}}
        default_guild = {"archive": 60}
        self.config.register_channel(**default_channel)
        self.config.register_guild(**default_guild)

    @commands.group()
    @commands.guild_only()
    @checks.admin_or_permissions(administrator=True)
    async def threadset(self, ctx):
        """
        Manage threads
        """
        pass

    @threadset.command(name="list")
    async def threadset_list(self, ctx):
        """
        List all channels and their thread roles
        """
        channel_data = await self.config.all_channels()

        msg = ""
        for id, data in channel_data.items():
            channel = ctx.guild.get_channel(id)
            if not channel:
                continue

            msg += f"#{channel.name}\n"
            for role_id, num_threads in data["allowed_roles"].items():
                role = ctx.guild.get_role(int(role_id))
                if not role:
                    continue
                msg += f"\t- @{role.name}: {num_threads}\n"
            msg += "\n"

        pages = list(pagify(msg, page_length=1700, delims=["\n"], priority=True))
        pages = [box(f"{page}\n\n-----------------\nPage {i+1} of {len(pages)}") for i, page in enumerate(pages)]

        if not pages:
            await ctx.send("No channels setup to use thread manager.", delete_after=30)
        else:
            await menu(ctx, pages, DEFAULT_CONTROLS)

    @threadset.command(name="archive")
    async def threadset_archive(self, ctx, archive: int):
        """
        Set the archive duration of user created threads

        Must be one of: 60, 1440, 4320, and 10080
        If your guild doesn't have longer thread archival features, the archive value is clipped to the highest value available.
        """
        if archive not in [60, 1440, 4320, 10080]:
            return await ctx.send("Invalid archive time, try again.", delete_after=30)

        await self.config.guild(ctx.guild).archive.set(archive)
        await ctx.tick()

    @threadset.command(name="add")
    async def threadset_add(self, ctx, channel: discord.TextChannel, num_threads: int, *, role: discord.Role):
        """
        Set the number for threads anyone with role can create for channel

        If a user has multiple roles, whatever role has the highest value is used
        """
        async with self.config.channel(channel).allowed_roles() as allowed_roles:
            allowed_roles[str(role.id)] = num_threads
        await ctx.tick()

    @threadset.command(name="del")
    async def threadset_del(self, ctx, channel: discord.TextChannel, *, role: discord.Role):
        """
        Delete a role from a channel

        Does not cleanup threads currently active
        """
        async with self.config.channel(channel).allowed_roles() as allowed_roles:
            if str(role.id) in allowed_roles:
                del allowed_roles[str(role.id)]

        await ctx.tick()

    @commands.command()
    @commands.guild_only()
    async def thread(self, ctx, *, name: str):
        """
        Create a new thread from this channel

        You must have proper permissions set
        """
        channel = ctx.channel
        guild = ctx.guild
        user = ctx.author

        allowed_roles = await self.config.channel(channel).allowed_roles()
        roles = {int(r) for r in allowed_roles.keys()}
        u_roles = {r.id for r in user.roles}

        if not (roles & u_roles):
            return await ctx.send(
                "Sorry, you do not have a role that allows you to create threads here.", delete_after=15
            )

        possible_roles = roles & u_roles
        num_threads = sorted([allowed_roles[str(r)] for r in possible_roles])[-1]

        threads = await self.config.channel(channel).threads()
        if str(user.id) not in threads:
            threads[str(user.id)] = []

        user_threads = threads[str(user.id)]
        if len(user_threads) >= num_threads:
            # first, need to update active threads for this channel
            activate_threads = set(await get_active_threads(self.bot, guild))
            still_active = set(user_threads) & activate_threads

            # remove not active threads
            user_threads = [t for t in user_threads if t in still_active]
            # update config
            threads[str(user.id)] = user_threads
            await self.config.channel(channel).threads.set(threads)

        if len(user_threads) >= num_threads:
            return await ctx.send(
                f"You have reached the maximum number ({num_threads}) of threads you can create for this channel. Please have a staff member archive one of your threads.",
                delete_after=15,
            )

        # now we can create a thread
        archive = await self.config.guild(guild).archive()
        try:
            thread = await create_thread(self.bot, channel, ctx.message, name=name, archive=archive)
            await add_user_thread(self.bot, thread, user)
        except:
            return await ctx.send(
                "Something went wrong, most likely a permissions issue. Please contact a staff member.", delete_after=30
            )

        threads[str(user.id)].append(thread)
        await self.config.channel(channel).threads.set(threads)
