import asyncio
import discord

from typing import Optional, Literal
from discord.utils import get
from datetime import timedelta

from redbot.core import Config, checks, commands
from redbot.core.utils.predicates import MessagePredicate, ReactionPredicate
from redbot.core.utils.menus import start_adding_reactions
from redbot.core.utils.antispam import AntiSpam

from redbot.core.bot import Red
from .discord_thread_feature import create_thread, add_user_thread


class Suggestion(commands.Cog):
    """
    Simple suggestion box, basically.

    **Use `[p]setsuggest setup` first.**
    Only admins can approve or reject suggestions.
    """

    __author__ = "saurichable"
    __version__ = "1.4.0"

    def __init__(self, bot: Red):
        self.bot = bot
        self.config = Config.get_conf(self, identifier=2115656421364, force_registration=True)
        self.antispam = {}
        self.config.register_guild(
            same=False,
            suggest_id=None,
            approve_id=None,
            reject_id=None,
            next_id=1,
            up_emoji=None,
            down_emoji=None,
            delete_suggest=False,
            create_threads=False,
        )
        self.config.register_global(toggle=False, server_id=None, channel_id=None, next_id=1, ignore=[])
        self.config.init_custom("SUGGESTION", 2)
        self.config.register_custom(
            "SUGGESTION",
            author=[],
            msg_id=0,
            finished=False,
            approved=False,
            rejected=False,
            reason=False,
            stext=None,
            rtext=None,
            num_up=0,
            num_down=0,
        )

    @commands.command()
    @commands.guild_only()
    @checks.bot_has_permissions(add_reactions=True)
    async def suggest(self, ctx: commands.Context, *, suggestion: str):
        """Suggest something. Message is required."""
        suggest_id = await self.config.guild(ctx.guild).suggest_id()
        if not suggest_id:
            if await self.config.toggle():
                if ctx.guild.id in await self.config.ignore():
                    return await ctx.send("Uh oh, suggestions aren't enabled.")
                global_guild = self.bot.get_guild(await self.config.server_id())
                channel = get(global_guild.text_channels, id=await self.config.channel_id())
            else:
                return await ctx.send("Uh oh, suggestions aren't enabled.")
        else:
            channel = get(ctx.guild.text_channels, id=suggest_id)
        if not channel:
            return await ctx.send("Uh oh, looks like your Admins haven't added the required channel.")
        if ctx.guild not in self.antispam:
            self.antispam[ctx.guild] = {}
        if ctx.author not in self.antispam[ctx.guild]:
            self.antispam[ctx.guild][ctx.author] = AntiSpam([(timedelta(days=1), 6)])
        if self.antispam[ctx.guild][ctx.author].spammy:
            return await ctx.send("Uh oh, you're doing this way too frequently.")
        embed = discord.Embed(color=await ctx.embed_colour(), description=suggestion)
        embed.set_author(
            name=f"Suggestion by {ctx.author.display_name}",
            icon_url=ctx.author.avatar_url,
        )
        embed.set_footer(text=f"Suggested by {ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id})")

        if not suggest_id:
            if await self.config.toggle():
                s_id = await self.config.next_id()
                await self.config.next_id.set(s_id + 1)
                server = 1
                content = f"Global suggestion #{s_id}"
        else:
            s_id = await self.config.guild(ctx.guild).next_id()
            await self.config.guild(ctx.guild).next_id.set(s_id + 1)
            server = ctx.guild.id
            content = f"Suggestion #{s_id}"
        msg = await channel.send(content=content, embed=embed)

        up_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).up_emoji())
        if not up_emoji:
            up_emoji = "✅"
        down_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).down_emoji())
        if not down_emoji:
            down_emoji = "❎"
        await msg.add_reaction(up_emoji)
        await msg.add_reaction(down_emoji)

        async with self.config.custom("SUGGESTION", server, s_id).author() as author:
            author.append(ctx.author.id)
            author.append(ctx.author.name)
            author.append(ctx.author.discriminator)
        await self.config.custom("SUGGESTION", server, s_id).stext.set(suggestion)
        await self.config.custom("SUGGESTION", server, s_id).msg_id.set(msg.id)

        self.antispam[ctx.guild][ctx.author].stamp()
        if await self.config.guild(ctx.guild).delete_suggest():
            await ctx.message.delete()
        else:
            await ctx.tick()

        if await self.config.guild(ctx.guild).create_threads():
            # always use max archive, function will clip it if needed
            try:
                thread = await create_thread(self.bot, channel, msg, name=content, archive=10080)
                await add_user_thread(self.bot, thread, ctx.author)
            except:
                await ctx.send("Error in creating a thread for this suggestion, please check permissions!")

        try:
            await ctx.author.send(content="Your suggestion has been sent for approval!", embed=embed)
        except discord.Forbidden:
            pass

    @checks.admin_or_permissions(administrator=True)
    @commands.command()
    @commands.guild_only()
    @checks.bot_has_permissions(manage_messages=True)
    async def approve(
        self,
        ctx: commands.Context,
        suggestion_id: int,
        is_global: Optional[bool] = False,
        *,
        reason: str = "",
    ):
        """Approve a suggestion."""
        if is_global:
            if await self.config.toggle():
                if ctx.author.id != self.bot.owner_id:
                    return await ctx.send("Uh oh, you're not my owner.")
                server = 1
                global_guild = self.bot.get_guild(await self.config.server_id())
                oldchannel = get(global_guild.text_channels, id=await self.config.channel_id())
            else:
                return await ctx.send("Global suggestions aren't enabled.")
        else:
            server = ctx.guild.id
            oldchannel = get(
                ctx.guild.text_channels,
                id=await self.config.guild(ctx.guild).suggest_id(),
            )
            channel = get(
                ctx.guild.text_channels,
                id=await self.config.guild(ctx.guild).approve_id(),
            )
        msg_id = await self.config.custom("SUGGESTION", server, suggestion_id).msg_id()
        if msg_id != 0:
            if await self.config.custom("SUGGESTION", server, suggestion_id).finished():
                return await ctx.send("This suggestion has been finished already.")
        try:
            oldmsg = await oldchannel.fetch_message(id=msg_id)
        except discord.NotFound:
            return await ctx.send("Uh oh, message with this ID doesn't exist.")
        if not oldmsg:
            return await ctx.send("Uh oh, message with this ID doesn't exist.")
        embed = oldmsg.embeds[0]
        content = oldmsg.content
        # change to green for Approved
        embed.color = discord.Color.from_rgb(0, 255, 0)
        op_info = await self.config.custom("SUGGESTION", server, suggestion_id).author()
        op_id = int(op_info[0])
        op = await self.bot.fetch_user(op_id)
        op_name = op.name
        op_avatar = op.avatar_url
        if not op:
            op_name = str(op_info[1])
            op_avatar = ctx.guild.icon_url
        embed.set_author(name=f"Approved suggestion by {op_name}", icon_url=op_avatar)
        # get number of reactions for approved / denied
        up_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).up_emoji())
        if not up_emoji:
            up_emoji = "✅"
        down_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).down_emoji())
        if not down_emoji:
            down_emoji = "❎"
        reactions = oldmsg.reactions
        num_up = 0
        num_down = 0
        for react in reactions:
            if react.emoji == up_emoji:
                num_up = react.count - 1  # minus 1 for bot
            elif react.emoji == down_emoji:
                num_down = react.count - 1  # minus 1 for bot

        await self.config.custom("SUGGESTION", server, suggestion_id).num_up.set(num_up)
        await self.config.custom("SUGGESTION", server, suggestion_id).num_down.set(num_down)
        embed.add_field(name="Votes:", value=f"For: `{num_up}`, Against: `{num_down}`", inline=False)

        if reason:
            embed.add_field(name="Reason:", value=reason, inline=False)
            await self.config.custom("SUGGESTION", server, suggestion_id).reason.set(True)
            await self.config.custom("SUGGESTION", server, suggestion_id).rtext.set(reason)
        if is_global:
            await oldmsg.edit(content=content, embed=embed)
            try:
                await oldmsg.clear_reactions()
            except discord.Forbidden:
                pass
        else:
            if channel:
                await oldmsg.delete()
                nmsg = await channel.send(content=content, embed=embed)
                await self.config.custom("SUGGESTION", server, suggestion_id).msg_id.set(nmsg.id)
            else:
                if not await self.config.guild(ctx.guild).same():
                    await oldmsg.delete()
                    await self.config.custom("SUGGESTION", server, suggestion_id).msg_id.set(1)
                else:
                    await oldmsg.edit(content=content, embed=embed)
                    try:
                        await oldmsg.clear_reactions()
                    except discord.Forbidden:
                        pass
        await self.config.custom("SUGGESTION", server, suggestion_id).finished.set(True)
        await self.config.custom("SUGGESTION", server, suggestion_id).approved.set(True)
        await ctx.tick()

        try:
            await op.send(content="Your suggestion has been approved!", embed=embed)
        except discord.Forbidden:
            pass

    @checks.admin_or_permissions(administrator=True)
    @commands.command()
    @commands.guild_only()
    @checks.bot_has_permissions(manage_messages=True)
    async def reject(
        self,
        ctx: commands.Context,
        suggestion_id: int,
        is_global: Optional[bool] = False,
        *,
        reason: str = "",
    ):
        """Reject a suggestion. Reason is optional."""
        if is_global:
            if await self.config.toggle():
                if ctx.author.id != self.bot.owner_id:
                    return await ctx.send("Uh oh, you're not my owner.")
                server = 1
                global_guild = self.bot.get_guild(await self.config.server_id())
                oldchannel = get(global_guild.text_channels, id=await self.config.channel_id())
            else:
                return await ctx.send("Global suggestions aren't enabled.")
        else:
            server = ctx.guild.id
            oldchannel = get(
                ctx.guild.text_channels,
                id=await self.config.guild(ctx.guild).suggest_id(),
            )
            channel = get(
                ctx.guild.text_channels,
                id=await self.config.guild(ctx.guild).reject_id(),
            )
        msg_id = await self.config.custom("SUGGESTION", server, suggestion_id).msg_id()
        if msg_id != 0:
            if await self.config.custom("SUGGESTION", server, suggestion_id).finished():
                return await ctx.send("This suggestion has been finished already.")
        try:
            oldmsg = await oldchannel.fetch_message(id=msg_id)
        except discord.NotFound:
            return await ctx.send("Uh oh, message with this ID doesn't exist.")
        if not oldmsg:
            return await ctx.send("Uh oh, message with this ID doesn't exist.")
        embed = oldmsg.embeds[0]
        content = oldmsg.content
        # change to red for denied
        embed.color = discord.Color.from_rgb(255, 0, 0)
        op_info = await self.config.custom("SUGGESTION", server, suggestion_id).author()
        op_id = int(op_info[0])
        op = await self.bot.fetch_user(op_id)
        op_name = op.name
        op_avatar = op.avatar_url
        if not op:
            op_name = str(op_info[1])
            op_avatar = ctx.guild.icon_url
        embed.set_author(name=f"Rejected suggestion by {op_name}", icon_url=op_avatar)

        # get number of reactions for approved / denied
        up_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).up_emoji())
        if not up_emoji:
            up_emoji = "✅"
        down_emoji = self.bot.get_emoji(await self.config.guild(ctx.guild).down_emoji())
        if not down_emoji:
            down_emoji = "❎"

        reactions = oldmsg.reactions
        num_up = 0
        num_down = 0
        for react in reactions:
            if react.emoji == up_emoji:
                num_up = react.count - 1  # minus 1 for bot
            elif react.emoji == down_emoji:
                num_down = react.count - 1  # minus 1 for bot

        await self.config.custom("SUGGESTION", server, suggestion_id).num_up.set(num_up)
        await self.config.custom("SUGGESTION", server, suggestion_id).num_down.set(num_down)
        embed.add_field(name="Votes:", value=f"For: `{num_up}`, Against: `{num_down}`", inline=False)
        if reason:
            embed.add_field(name="Reason:", value=reason, inline=False)
            await self.config.custom("SUGGESTION", server, suggestion_id).reason.set(True)
            await self.config.custom("SUGGESTION", server, suggestion_id).rtext.set(reason)
        if is_global:
            await oldmsg.edit(content=content, embed=embed)
            try:
                await oldmsg.clear_reactions()
            except discord.Forbidden:
                pass
        else:
            if channel:
                await oldmsg.delete()
                nmsg = await channel.send(content=content, embed=embed)
                await self.config.custom("SUGGESTION", server, suggestion_id).msg_id.set(nmsg.id)
            else:
                if not await self.config.guild(ctx.guild).same():
                    await oldmsg.delete()
                    await self.config.custom("SUGGESTION", server, suggestion_id).msg_id.set(1)
                else:
                    await oldmsg.edit(content=content, embed=embed)
                    try:
                        await oldmsg.clear_reactions()
                    except discord.Forbidden:
                        pass
        await self.config.custom("SUGGESTION", server, suggestion_id).finished.set(True)
        await self.config.custom("SUGGESTION", server, suggestion_id).rejected.set(True)
        await ctx.tick()

        try:
            await op.send(content="Your suggestion has been rejected!", embed=embed)
        except discord.Forbidden:
            pass

    @checks.admin_or_permissions(administrator=True)
    @commands.command()
    @commands.guild_only()
    @checks.bot_has_permissions(manage_messages=True)
    async def addreason(
        self,
        ctx: commands.Context,
        suggestion_id: int,
        is_global: Optional[bool] = False,
        *,
        reason: str,
    ):
        """Add a reason to a suggestion.

        Only works for non global suggestions."""
        if is_global:
            if await self.config.toggle():
                if ctx.author.id != self.bot.owner_id:
                    return await ctx.send("Uh oh, you're not my owner.")
                server = 1
                global_guild = self.bot.get_guild(await self.config.server_id())
                channel = get(global_guild.text_channels, id=await self.config.channel_id())
            else:
                return await ctx.send("Global suggestions aren't enabled.")
        else:
            server = ctx.guild.id
            if not await self.config.guild(ctx.guild).same():
                channel = get(
                    ctx.guild.text_channels,
                    id=await self.config.guild(ctx.guild).reject_id(),
                )
            else:
                channel = get(
                    ctx.guild.text_channels,
                    id=await self.config.guild(ctx.guild).suggest_id(),
                )
        msg_id = await self.config.custom("SUGGESTION", server, suggestion_id).msg_id()
        if msg_id != 0:
            if await self.config.custom("SUGGESTION", server, suggestion_id).reason():
                return await ctx.send("This suggestion already has a reason.")
            content, embed = await self._build_suggestion(ctx, ctx.author.id, ctx.guild.id, suggestion_id, is_global)
            embed.add_field(name="Reason:", value=reason, inline=False)
            msg = await channel.fetch_message(id=msg_id)
            if msg:
                await msg.edit(content=content, embed=embed)
        await self.config.custom("SUGGESTION", server, suggestion_id).reason.set(True)
        await self.config.custom("SUGGESTION", server, suggestion_id).rtext.set(reason)
        await ctx.tick()

    @checks.admin_or_permissions(administrator=True)
    @commands.command()
    @commands.guild_only()
    async def showsuggestion(
        self,
        ctx: commands.Context,
        suggestion_id: int,
        is_global: Optional[bool] = False,
    ):
        """Show a suggestion."""
        content, embed = await self._build_suggestion(ctx, ctx.author.id, ctx.guild.id, suggestion_id, is_global)
        await ctx.send(content=content, embed=embed)

    @checks.admin_or_permissions(administrator=True)
    @commands.group(autohelp=True)
    @commands.guild_only()
    async def setsuggest(self, ctx: commands.Context):
        """Suggestion settings"""
        pass

    @setsuggest.command(name="threads")
    async def setsuggest_threads(self, ctx: commands.Context, toggle: bool):
        """
        Enable automatic thread creation for each suggestion, for discussion
        """
        await self.config.guild(ctx.guild).create_threads.set(toggle)
        await ctx.tick()

    @checks.bot_has_permissions(manage_channels=True)
    @setsuggest.command(name="setup")
    async def setsuggest_setup(self, ctx: commands.Context):
        """Go through the initial setup process."""
        await self.config.guild(ctx.guild).same.set(False)
        await self.config.guild(ctx.guild).suggest_id.set(None)
        await self.config.guild(ctx.guild).approve_id.set(None)
        await self.config.guild(ctx.guild).reject_id.set(None)
        predchan = MessagePredicate.valid_text_channel(ctx)
        overwrites = {
            ctx.guild.default_role: discord.PermissionOverwrite(send_messages=False),
            ctx.guild.me: discord.PermissionOverwrite(send_messages=True),
        }
        msg = await ctx.send("Do you already have your channel(s) done?")
        start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
        pred = ReactionPredicate.yes_or_no(msg, ctx.author)
        try:
            await self.bot.wait_for("reaction_add", timeout=30, check=pred)
        except asyncio.TimeoutError:
            await msg.delete()
            return await ctx.send("You took too long. Try again, please.")
        if not pred.result:
            await msg.delete()
            suggestions = get(ctx.guild.text_channels, name="suggestions")
            if not suggestions:
                suggestions = await ctx.guild.create_text_channel(
                    "suggestions", overwrites=overwrites, reason="Suggestion cog setup"
                )
            await self.config.guild(ctx.guild).suggest_id.set(suggestions.id)

            msg = await ctx.send(
                "Do you want to use the same channel for approved and rejected suggestions? (If yes, they won't be reposted anywhere, only their title will change accordingly.)"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await self.bot.wait_for("reaction_add", timeout=30, check=pred)
            except asyncio.TimeoutError:
                await msg.delete()
                return await ctx.send("You took too long. Try again, please.")
            if pred.result:
                await msg.delete()
                await self.config.guild(ctx.guild).same.set(True)
            else:
                await msg.delete()
                approved = get(ctx.guild.text_channels, name="approved-suggestions")
                if not approved:
                    msg = await ctx.send("Do you want to have an approved suggestions channel?")
                    start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
                    pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                    try:
                        await self.bot.wait_for("reaction_add", timeout=30, check=pred)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send("You took too long. Try again, please.")
                    if pred.result:
                        approved = await ctx.guild.create_text_channel(
                            "approved-suggestions",
                            overwrites=overwrites,
                            reason="Suggestion cog setup",
                        )
                        await self.config.guild(ctx.guild).approve_id.set(approved.id)
                    await msg.delete()
                else:
                    await self.config.guild(ctx.guild).approve_id.set(approved.id)
                rejected = get(ctx.guild.text_channels, name="rejected-suggestions")
                if not rejected:
                    msg = await ctx.send("Do you want to have a rejected suggestions channel?")
                    start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
                    pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                    try:
                        await self.bot.wait_for("reaction_add", timeout=30, check=pred)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send("You took too long. Try again, please.")
                    if pred.result:
                        rejected = await ctx.guild.create_text_channel(
                            "rejected-suggestions",
                            overwrites=overwrites,
                            reason="Suggestion cog setup",
                        )
                        await self.config.guild(ctx.guild).reject_id.set(rejected.id)
                    await msg.delete()
                else:
                    await self.config.guild(ctx.guild).reject_id.set(rejected.id)
        else:
            await msg.delete()
            msg = await ctx.send("Mention the channel where you want me to post new suggestions.")
            try:
                await self.bot.wait_for("message", timeout=30, check=predchan)
            except asyncio.TimeoutError:
                await msg.delete()
                return await ctx.send("You took too long. Try again, please.")
            suggestion = predchan.result
            await self.config.guild(ctx.guild).suggest_id.set(suggestion.id)
            await msg.delete()

            msg = await ctx.send(
                "Do you want to use the same channel for approved and rejected suggestions? (If yes, they won't be reposted anywhere, only their title will change accordingly.)"
            )
            start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
            pred = ReactionPredicate.yes_or_no(msg, ctx.author)
            try:
                await self.bot.wait_for("reaction_add", timeout=30, check=pred)
            except asyncio.TimeoutError:
                await msg.delete()
                return await ctx.send("You took too long. Try again, please.")
            if pred.result:
                await msg.delete()
                await self.config.guild(ctx.guild).same.set(True)
            else:
                await msg.delete()
                msg = await ctx.send("Do you want to have an approved suggestions channel?")
                start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
                pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                try:
                    await self.bot.wait_for("reaction_add", timeout=30, check=pred)
                except asyncio.TimeoutError:
                    await msg.delete()
                    return await ctx.send("You took too long. Try again, please.")
                if pred.result:
                    await msg.delete()
                    msg = await ctx.send("Mention the channel where you want me to post approved suggestions.")
                    try:
                        await self.bot.wait_for("message", timeout=30, check=predchan)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send("You took too long. Try again, please.")
                    approved = predchan.result
                    await self.config.guild(ctx.guild).approve_id.set(approved.id)
                await msg.delete()

                msg = await ctx.send("Do you want to have a rejected suggestions channel?")
                start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
                pred = ReactionPredicate.yes_or_no(msg, ctx.author)
                try:
                    await self.bot.wait_for("reaction_add", timeout=30, check=pred)
                except asyncio.TimeoutError:
                    await msg.delete()
                    return await ctx.send("You took too long. Try again, please.")
                if pred.result:
                    await msg.delete()
                    msg = await ctx.send("Mention the channel where you want me to post rejected suggestions.")
                    try:
                        await self.bot.wait_for("message", timeout=30, check=predchan)
                    except asyncio.TimeoutError:
                        await msg.delete()
                        return await ctx.send("You took too long. Try again, please.")
                    rejected = predchan.result
                    await self.config.guild(ctx.guild).reject_id.set(rejected.id)
                await msg.delete()

        msg = await ctx.send(
            "Do you want to automatically create threads for each suggestion to allow discussion of each suggestion to be seperated?"
        )
        start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
        pred = ReactionPredicate.yes_or_no(msg, ctx.author)
        try:
            await self.bot.wait_for("reaction_add", timeout=30, check=pred)
        except asyncio.TimeoutError:
            await msg.delete()
            return await ctx.send("You took too long. Try again, please.")
        if pred.result:
            await self.config.guild(ctx.guild).create_threads.set(True)

        await ctx.send("You have finished the setup! Please, move your channels to the category you want them in.")

    @checks.bot_has_permissions(add_reactions=True)
    @setsuggest.command(name="upemoji")
    async def setsuggest_upemoji(self, ctx: commands.Context, up_emoji: discord.Emoji = None):
        """Set custom reactions emoji instead of ✅."""
        if not up_emoji:
            await self.config.guild(ctx.guild).up_emoji.set(None)
        else:
            try:
                await ctx.message.add_reaction(up_emoji)
            except discord.HTTPException:
                return await ctx.send("Uh oh, I cannot use that emoji.")
            await self.config.guild(ctx.guild).up_emoji.set(up_emoji.id)
        await ctx.tick()

    @checks.bot_has_permissions(add_reactions=True)
    @setsuggest.command(name="downemoji")
    async def setsuggest_downemoji(self, ctx: commands.Context, down_emoji: discord.Emoji = None):
        """Set custom reactions emoji instead of ❎."""
        if not down_emoji:
            await self.config.guild(ctx.guild).up_emoji.set(None)
        else:
            try:
                await ctx.message.add_reaction(down_emoji)
            except discord.HTTPException:
                return await ctx.send("Uh oh, I cannot use that emoji.")
            await self.config.guild(ctx.guild).down_emoji.set(down_emoji.id)
        await ctx.tick()

    @checks.bot_has_permissions(manage_messages=True)
    @setsuggest.command(name="autodelete")
    async def setsuggest_autodelete(self, ctx: commands.Context, on_off: bool = None):
        """
        Toggle whether after `[p]suggest`, the bot deletes the message.
        """
        target_state = on_off if on_off else not (await self.config.guild(ctx.guild).delete_suggest())
        await self.config.guild(ctx.guild).delete_suggest.set(target_state)
        if target_state:
            await ctx.send("Auto deletion is now enabled.")
        else:
            await ctx.send("Auto deletion is now disabled.")

    @setsuggest.group(autohelp=True)
    @checks.is_owner()
    @commands.guild_only()
    async def setglobal(self, ctx: commands.Context):
        """Global suggestions settings.

        There is nothing like approved or rejected channels because global suggestions are meant to be for the bot only and will only work if it is sent in a server where normal suggestions are disabled."""
        pass

    @setglobal.command(name="toggle")
    async def setsuggest_setglobal_toggle(self, ctx: commands.Context, on_off: bool = None):
        """Toggle global suggestions.
        If `on_off` is not provided, the state will be flipped."""
        target_state = on_off if on_off else not (await self.config.toggle())
        await self.config.toggle.set(target_state)
        if target_state:
            await ctx.send("Global suggestions are now enabled.")
        else:
            await ctx.send("Global suggestions are now disabled.")

    @setglobal.command(name="channel")
    async def setsuggest_setglobal_channel(
        self,
        ctx: commands.Context,
        server: discord.Guild = None,
        channel: discord.TextChannel = None,
    ):
        """Add channel where global suggestions should be sent."""
        if not server:
            server = ctx.guild
        if not channel:
            channel = ctx.channel
        await self.config.server_id.set(server.id)
        await self.config.channel_id.set(channel.id)
        await ctx.send(f"{channel.mention} has been saved for global suggestions.")

    @setglobal.command(name="ignore")
    async def setsuggest_setglobal_ignore(self, ctx: commands.Context, server: discord.Guild = None):
        """Ignore suggestions from the server."""
        if not server:
            server = ctx.guild
        if server.id not in await self.config.ignore():
            async with self.config.ignore() as ignore:
                ignore.append(server.id)
            await ctx.send(f"{server.name} has been added into the ignored list.")
        else:
            await ctx.send(f"{server.name} is already in the ignored list.")

    @setglobal.command(name="unignore")
    async def setsuggest_setglobal_unignore(self, ctx: commands.Context, server: discord.Guild = None):
        """Remove server from the ignored list."""
        if not server:
            server = ctx.guild
        if server.id in await self.config.ignore():
            async with self.config.ignore() as ignore:
                ignore.remove(server.id)
            await ctx.send(f"{server.name} has been removed from the ignored list.")
        else:
            await ctx.send(f"{server.name} already isn't in the ignored list.")

    async def _build_suggestion(self, ctx, author_id, server_id, suggestion_id, is_global):
        if is_global:
            if await self.config.toggle():
                if author_id != self.bot.owner_id:
                    return await ctx.send("Uh oh, you're not my owner.")
                server = 1
                if await self.config.custom("SUGGESTION", server, suggestion_id).msg_id() != 0:
                    content = f"Global suggestion #{suggestion_id}"
                else:
                    return await ctx.send("Uh oh, that suggestion doesn't seem to exist.")
            else:
                return await ctx.send("Global suggestions aren't enabled.")
        if not is_global:
            server = server_id
            if await self.config.custom("SUGGESTION", server, suggestion_id).msg_id() != 0:
                content = f"Suggestion #{suggestion_id}"
            else:
                return await ctx.send("Uh oh, that suggestion doesn't seem to exist.")
        op_info = await self.config.custom("SUGGESTION", server, suggestion_id).author()
        op_id = int(op_info[0])
        op = await self.bot.fetch_user(op_id)
        if op:
            op_name = op.name
            op_discriminator = op.discriminator
            op_avatar = op.avatar_url
        else:
            op_name = str(op_info[1])
            op_discriminator = int(op_info[2])
            op_avatar = ctx.guild.icon_url

        if not await self.config.custom("SUGGESTION", server, suggestion_id).finished():
            atext = f"Suggestion by {op_name}"
        else:
            if await self.config.custom("SUGGESTION", server, suggestion_id).approved():
                atext = f"Approved suggestion by {op_name}"
            elif await self.config.custom("SUGGESTION", server, suggestion_id).rejected():
                atext = f"Rejected suggestion by {op_name}"

        embed = discord.Embed(
            color=await ctx.embed_colour(),
            description=await self.config.custom("SUGGESTION", server, suggestion_id).stext(),
        )
        embed.set_author(name=atext, icon_url=op_avatar)
        embed.set_footer(text=f"Suggested by {op_name}#{op_discriminator} ({op_id})")

        if await self.config.custom("SUGGESTION", server, suggestion_id).reason():
            embed.add_field(
                name="Reason:",
                value=await self.config.custom("SUGGESTION", server, suggestion_id).rtext(),
                inline=False,
            )

        if await self.config.custom("SUGGESTION", server, suggestion_id).finished():
            num_up = await self.config.custom("SUGGESTION", server, suggestion_id).num_up()
            num_down = await self.config.custom("SUGGESTION", server, suggestion_id).num_down()
            embed.add_field(name="Votes:", value=f"For: `{num_up}`, Against: `{num_down}`", inline=False)

        return content, embed

    async def red_delete_data_for_user(
        self,
        *,
        requester: Literal["discord_deleted_user", "owner", "user", "user_strict"],
        user_id: int,
    ):
        pass
