# pylint: disable=abstract-method
from datetime import timedelta
from math import ceil
from time import time
from typing import Tuple

from discord import AllowedMentions, File, Interaction, Member, Object, Role, TextChannel, User
from discord.abc import Messageable
from discord.errors import Forbidden, HTTPException, NotFound
from redbot.core import app_commands, commands
from redbot.core.bot import Red
from redbot.core.commands.converter import parse_relativedelta, parse_timedelta
from redbot.core.utils.chat_formatting import bold, error, humanize_timedelta, inline

from ..utilities.config import config
from ..utilities.factory import message_factory, resolve_factory
from ..utilities.logger import logger
from ..utilities.utils import get_footer_image, log, send_evidenceformat, timedelta_from_relativedelta
from .moderation import Moderation
from .type import Type, type_registry


def get_icon(bot: Red) -> File:
    cog = bot.get_cog("Aurora")
    if cog:
        return get_footer_image(cog)
    raise ValueError("Aurora cog not found. How was this managed?")

class Note(Type):
    key="note"
    string="note"
    verb="noted"

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str) -> 'Note':
        response = await ctx.send(
            content=f"{target.mention} has {cls.embed_desc}{cls.verb}!\n**Reason** - `{reason}`"
        )

        if silent is False:
            try:
                embed = await message_factory(
                    bot=ctx.bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    moderator=ctx.author,
                    reason=reason,
                    moderation_type=cls(),
                    response=response,
                )
                await target.send(embed=embed, file=get_icon(ctx.bot))
            except HTTPException:
                pass

        moderation = await Moderation.log(
            bot=ctx.bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type="user",
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason,
        )
        await response.edit(
            content=f"{target.mention} has {cls.embed_desc}{cls.verb}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`"
        )
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        if await config.guild(moderation.guild).dm_users() is True:
            try:
                target = await moderation.bot.fetch_user(moderation.target_id)
                embed = await resolve_factory(
                    moderation=moderation,
                    reason=reason
                )
                await target.send(embed=embed, file=get_icon(bot=moderation.bot))
            except (Forbidden, HTTPException, NotFound):
                pass
        return True, ""

class Warn(Type):
    key="warn"
    string="warn"
    verb="warned"

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str) -> 'Warn':
        response = await ctx.send(
            content=f"{target.mention} has {cls.embed_desc}{cls.verb}!\n**Reason** - `{reason}`"
        )

        if silent is False:
            try:
                embed = await message_factory(
                    bot=ctx.bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    moderator=ctx.author,
                    reason=reason,
                    moderation_type=cls(),
                    response=response,
                )
                await target.send(embed=embed, file=get_icon(ctx.bot))
            except HTTPException:
                pass

        moderation = await Moderation.log(
            bot=ctx.bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type="user",
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason,
        )
        await response.edit(
            content=f"{target.mention} has {cls.embed_desc}{cls.verb}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`"
        )
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        if await config.guild(moderation.guild).dm_users() is True:
            try:
                target = await moderation.bot.fetch_user(moderation.target_id)
                embed = await resolve_factory(
                    moderation=moderation,
                    reason=reason
                )
                await target.send(embed=embed, file=get_icon(bot=moderation.bot))
            except (Forbidden, HTTPException, NotFound):
                pass
        return True, ""

class AddRole(Type):
    key="addrole"
    string="addrole"
    verb="added a role to"
    embed_desc="been given the "

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member, role: Role, silent: bool, duration: str | None = None, reason: str | None = None):
        addrole_whitelist = await config.guild(ctx.guild).addrole_whitelist()

        if not addrole_whitelist:
            await ctx.send(
                content=error("There are no whitelisted roles set for this server!"),
                ephemeral=True,
            )
            return

        if duration is not None:
            try:
                parsed_time = parse_relativedelta(argument=duration)
                if parsed_time is None:
                    raise commands.BadArgument()
                parsed_time = timedelta_from_relativedelta(relativedelta=parsed_time)
            except (commands.BadArgument, ValueError):
                await ctx.send(content=error(text="Please provide a valid duration!"), ephemeral=True)
                return cls()
        else:
            parsed_time = None

        if role.id not in addrole_whitelist:
            await ctx.send(
                content=error("That role isn't whitelisted!"), ephemeral=True
            )
            return

        if role.id in [user_role.id for user_role in target.roles]:
            await ctx.send(
                content=error(f"{target.mention} already has this role!"),
                ephemeral=True,
            )
            return

        response = await ctx.send(
            content=f"{target.mention} has {cls.embed_desc}{role.mention} role{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time else ''}!\n**Reason** - `{reason}`"
        )

        if silent is False:
            try:
                embed = await message_factory(
                    bot=ctx.bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    moderator=ctx.author,
                    reason=reason,
                    moderation_type=cls(),
                    response=response,
                    duration=parsed_time,
                )
                await target.send(embed=embed, file=get_icon(ctx.bot))
            except HTTPException:
                pass

        await target.add_roles(
            role,
            reason=f"Role added by {ctx.author.id}{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time else ''} for: {reason}",
        )

        moderation = await Moderation.log(
            bot=ctx.bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type="user",
            target_id=target.id,
            role_id=role.id,
            duration=parsed_time,
            reason=reason,
        )
        await response.edit(
            content=f"{target.mention} has {cls.embed_desc}{role.mention} role{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time else ''}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`",
        )
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def duration_edit_handler(cls, interaction: Interaction, old_moderation: Moderation, new_moderation: Moderation) -> bool: # pylint: disable=unused-argument
        return True

    @classmethod
    async def expiry_handler(cls, moderation: Moderation) -> int:
        try:
            target = moderation.guild.get_member(moderation.target_id)
            if not target:
                try:
                    target = await moderation.guild.fetch_member(moderation.target_id)
                except NotFound:
                    return 0

            await target.remove_roles(
                Object(moderation.role_id), reason=f"Automatic role removal from case #{moderation.id}"
            )

            if await config.guild(moderation.guild).dm_users() is True:
                try:
                    embed = await message_factory(
                        bot=moderation.bot,
                        color=await moderation.bot.get_embed_color(moderation.guild.channels[0]),
                        guild=moderation.guild,
                        reason=f"Automatic role removal from case #{moderation.id}",
                        moderation_type=type_registry["removerole"],
                        moderator=None,
                        duration=None,
                        response=None,
                        case=False,
                    )
                    await target.send(embed=embed, file=get_icon(bot=moderation.bot))
                except HTTPException:
                    pass
            logger.trace(
                "Removed role %s from %s (%s)",
                moderation.role_id,
                target.name,
                target.id,
            )
            return 1
        except (
            NotFound,
            Forbidden,
            HTTPException,
        ) as e:
            logger.error(
                "Removing the role %s from user %s failed due to: \n%s",
                moderation.role_id,
                moderation.target_id,
                e,
            )
            return 0

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        try:
            target = await moderation.guild.fetch_member(moderation.target_id)
            await target.remove_roles(
                Object(moderation.role_id), reason=reason
            )
            if await config.guild(moderation.guild).dm_users() is True:
                try:
                    embed = await resolve_factory(
                        moderation=moderation,
                        reason=reason
                    )
                    await target.send(embed=embed, file=get_icon(bot=moderation.bot))
                except HTTPException:
                    pass
                logger.trace(
                    "Removed role %s from %s (%s)",
                    moderation.role_id,
                    target.name,
                    target.id,
                )
                return True, ""
        except (NotFound, Forbidden, HTTPException) as e:
            logger.error(
                "Failed to remove role %s from user %s (%s)\n%s",
                moderation.role_id,
                target.name,
                target.id,
                e,
            )
            return False, "Failed to remove role from user."

class RemoveRole(Type):
    key="removerole"
    string="removerole"
    verb="removed a role from"
    embed_desc="had the "

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member, role: Role, silent: bool, duration: str | None = None, reason: str | None = None):
        addrole_whitelist = await config.guild(ctx.guild).addrole_whitelist()

        if not addrole_whitelist:
            await ctx.send(
                content=error("There are no whitelisted roles set for this server!"),
                ephemeral=True,
            )
            return

        if duration is not None:
            try:
                parsed_time = parse_relativedelta(argument=duration)
                if parsed_time is None:
                    raise commands.BadArgument()
                parsed_time = timedelta_from_relativedelta(relativedelta=parsed_time)
            except (commands.BadArgument, ValueError):
                await ctx.send(content=error(text="Please provide a valid duration!"), ephemeral=True)
                return cls()
        else:
            parsed_time = None

        if role.id not in addrole_whitelist:
            await ctx.send(
                content=error("That role isn't whitelisted!"), ephemeral=True
            )
            return

        if role.id not in [user_role.id for user_role in target.roles]:
            await ctx.send(
                content=error(f"{target.mention} does not have this role!"),
                ephemeral=True,
            )
            return

        response = await ctx.send(
            content=f"{target.mention} has {cls.embed_desc}{role.mention} role removed{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time else ''}!\n**Reason** - `{reason}`"
        )

        if silent is False:
            try:
                embed = await message_factory(
                    bot=ctx.bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    moderator=ctx.author,
                    reason=reason,
                    moderation_type=cls(),
                    response=response,
                    duration=parsed_time,
                )
                await target.send(embed=embed, file=get_icon(ctx.bot))
            except HTTPException:
                pass

        await target.remove_roles(
            role,
            reason=f"Role removed by {ctx.author.id}{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time else ''} for: {reason}",
        )

        moderation = await Moderation.log(
            bot=ctx.bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type="user",
            target_id=target.id,
            role_id=role.id,
            duration=parsed_time,
            reason=reason,
        )
        await response.edit(
            content=f"{target.mention} has {cls.embed_desc}{role.mention} role removed{' for ' + humanize_timedelta(timedelta=parsed_time) if parsed_time else ''}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`",
        )
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def duration_edit_handler(cls, interaction: Interaction, old_moderation: Moderation, new_moderation: Moderation) -> bool: # pylint: disable=unused-argument
        return True

    @classmethod
    async def expiry_handler(cls, moderation: Moderation) -> int:
        try:
            target = moderation.guild.get_member(moderation.target_id)
            if not target:
                try:
                    target = await moderation.guild.fetch_member(moderation.target_id)
                except NotFound:
                    return 0

            await target.add_roles(
                Object(moderation.role_id), reason=f"Automatic role addition from case #{moderation.id}"
            )
            if await config.guild(moderation.guild).dm_users() is True:
                try:
                    embed = await message_factory(
                        bot=moderation.bot,
                        color=await moderation.bot.get_embed_color(moderation.guild.channels[0]),
                        guild=moderation.guild,
                        reason=f"Automatic role addition from case #{moderation.id}",
                        moderation_type=type_registry["addrole"],
                        moderator=None,
                        duration=None,
                        response=None,
                        case=False,
                    )
                    await target.send(embed=embed, file=get_icon(bot=moderation.bot))
                except HTTPException:
                    pass
            logger.trace(
                "Added role %s to %s (%s)",
                moderation.role_id,
                target.name,
                target.id,
            )
            return 1
        except (
            NotFound,
            Forbidden,
            HTTPException,
        ) as e:
            logger.error(
                "Adding the role %s to user %s failed due to: \n%s",
                moderation.role_id,
                moderation.target_id,
                e,
            )
            return 0

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        try:
            target = await moderation.get_target()
            await target.add_roles(
                Object(moderation.role_id), reason=reason
            )
            if await config.guild(moderation.guild).dm_users() is True:
                try:
                    embed = await resolve_factory(
                        moderation=moderation,
                        reason=reason
                    )
                    await target.send(embed=embed, file=get_icon(bot=moderation.bot))
                except HTTPException:
                    pass
                logger.trace(
                    "Added role %s to %s (%s)",
                    moderation.role_id,
                    target.name,
                    target.id,
                )
                return True, ""
        except (NotFound, Forbidden, HTTPException) as e:
            logger.error(
                "Failed to add role %s to user %s (%s)\n%s",
                moderation.role_id,
                target.name,
                target.id,
                e,
            )
            return False, "Failed to add role to user."

class Mute(Type):
    key="mute"
    string="mute"
    verb="muted"

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member, silent: bool, duration: str, reason: str = None):
        if target.is_timed_out() is True:
            await ctx.send(
                error(f"{target.mention} is already muted!"),
                allowed_mentions=AllowedMentions(users=False),
                ephemeral=True,
            )
            return

        try:
            parsed_time = parse_timedelta(duration, maximum=timedelta(days=28))
            if parsed_time is None:
                await ctx.send(
                    error("Please provide a valid duration!"), ephemeral=True
                )
                return
        except commands.BadArgument:
            await ctx.send(
                error("Please provide a duration that is less than 28 days."), ephemeral=True
            )
            return

        await target.timeout(
            parsed_time, reason=f"Muted by {ctx.author.id} for: {reason}"
        )

        response = await ctx.send(
            content=f"{target.mention} has been muted for {humanize_timedelta(timedelta=parsed_time)}!\n**Reason** - `{reason}`"
        )

        if silent is False:
            try:
                embed = await message_factory(
                    bot=ctx.bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    moderator=ctx.author,
                    reason=reason,
                    moderation_type=cls(),
                    response=response,
                    duration=parsed_time,
                )
                await target.send(embed=embed, file=get_icon(ctx.bot))
            except HTTPException:
                pass

        moderation = await Moderation.log(
            bot=ctx.bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type="user",
            target_id=target.id,
            role_id=None,
            duration=parsed_time,
            reason=reason,
        )
        await response.edit(
            content=f"{target.mention} has been muted for {humanize_timedelta(timedelta=parsed_time)}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`"
        )
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        try:
            target = await moderation.guild.fetch_member(moderation.target_id)
        except (Forbidden, HTTPException, NotFound):
            return False, "User is not in the server, so I cannot unmute them."
        if target.is_timed_out() is False:
            return True, ""
        await target.timeout(None, reason=reason)

        if await config.guild(moderation.guild).dm_users() is True:
            try:
                embed = await resolve_factory(
                    moderation=moderation,
                    reason=reason
                )
                await target.send(embed=embed, file=get_icon(bot=moderation.bot))
            except (Forbidden, HTTPException):
                pass
        return True, ""

    @classmethod
    async def duration_edit_handler(cls, interaction: Interaction, old_moderation: Moderation, new_moderation: Moderation) -> bool: # pylint: disable=unused-argument
        if (
            time() - new_moderation.unix_timestamp
        ) + new_moderation.duration.total_seconds() > 2419200:
            await interaction.response.send_message(
                content=error(
                    "Please provide a duration that is less than 28 days from the initial moderation."
                ),
                ephemeral=True
            )
            return False

        try:
            member = await interaction.guild.fetch_member(
                new_moderation.target_id
            )

            await member.timeout(
                new_moderation.duration,
                reason=f"Case #{new_moderation.id:,} edited by {interaction.user.id}",
            )
        except NotFound:
            pass
        return True

class Unmute(Type):
    key="unmute"
    string="unmute"
    verb="unmuted"

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member, silent: bool, reason: str = None):
        if target.is_timed_out() is False:
            await ctx.send(
                content=error(f"{target.mention} is not muted!"),
                allowed_mentions=AllowedMentions(users=False),
                ephemeral=True,
            )
            return

        if reason:
            await target.timeout(
                None, reason=f"{cls.verb.title()} by {ctx.author.id} for: {reason}"
            )
        else:
            await target.timeout(None, reason=f"{cls.verb.title()} by {ctx.author.id}")
            reason = "No reason given."

        response_message = await ctx.send(
            content=f"{target.mention} has been {cls.verb}!\n**Reason** - `{reason}`"
        )

        if silent is False:
            try:
                embed = await message_factory(
                    bot=ctx.bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    moderator=ctx.author,
                    reason=reason,
                    moderation_type=cls(),
                    response=response_message,
                )
                await target.send(embed=embed, file=get_icon(ctx.bot))
            except HTTPException:
                pass

        moderation = await Moderation.log(
            bot=ctx.bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type="user",
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason,
        )
        await response_message.edit(
            content=f"{target.mention} has been {cls.verb}! (Case `#{moderation.id:,}`)\n**Reason** - `{reason}`"
        )
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)

class Kick(Type):
    key="kick"
    string="kick"
    verb="kicked"
    removes_from_guild=True

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str = None) -> 'Kick':
        """Kick a user."""
        bot = ctx.bot
        response_message = await ctx.send(f"{target.mention} has been {cls.verb}!\n{bold('Reason:')} {inline(reason)}")

        if silent is False:
            try:
                embed = await message_factory(
                    bot=bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    reason=reason,
                    moderation_type=cls(),
                    moderator=ctx.author,
                    duration=None,
                    response=response_message
                )
                await target.send(embed=embed, file=get_icon(bot))
            except HTTPException:
                pass

        await target.kick(reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}")
        moderation = await Moderation.log(
            bot=bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type='user',
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason
        )
        await response_message.edit(content=f"{target.mention} has been {cls.verb}! (Case {inline(f'#{moderation.id}')})\n{bold('Reason:')} {inline(reason)}")
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        if await config.guild(moderation.guild).dm_users() is True:
            try:
                target = await moderation.bot.fetch_user(moderation.target_id)
                embed = await resolve_factory(
                    moderation=moderation,
                    reason=reason
                )
                await target.send(embed=embed, file=get_icon(bot=moderation.bot))
            except (Forbidden, HTTPException, NotFound):
                pass
        return True, ""

class Ban(Type):
    key="ban"
    string="ban"
    verb="banned"
    removes_from_guild=True

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str = None, delete_messages: app_commands.Choice | None = None) -> 'Ban':
        """Ban a user."""
        bot = ctx.bot
        try:
            await ctx.guild.fetch_ban(target)
            await ctx.send(content=error(f"{target.mention} is already {cls.verb}!"), ephemeral=True)
            return
        except NotFound:
            pass

        if delete_messages is None:
            delete_messages_seconds = 0
        else:
            delete_messages_seconds = delete_messages.value

        response_message = await ctx.send(f"{target.mention} has been {cls.verb}!\n{bold('Reason:')} {inline(reason)}")

        if silent is False:
            try:
                embed = await message_factory(
                    bot=bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    reason=reason,
                    moderation_type=cls(),
                    moderator=ctx.author,
                    duration=None,
                    response=response_message
                )
                await target.send(embed=embed, file=get_icon(bot))
            except HTTPException:
                pass

        await ctx.guild.ban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}", delete_message_seconds=delete_messages_seconds)
        moderation = await Moderation.log(
            bot=bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type='user',
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason
        )
        await response_message.edit(content=f"{target.mention} has been {cls.verb}! (Case {inline(f'#{moderation.id}')})\n{bold('Reason:')} {inline(reason)}")
        await log(ctx=ctx, moderation_id=moderation.id)
        await send_evidenceformat(ctx=ctx, moderation_id=moderation.id)
        return cls()

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        try:
            target = await moderation.bot.fetch_user(moderation.target_id)
        except (HTTPException, NotFound):
            return False, "Fetching the target failed, so I cannot unban them."

        try:
            await moderation.guild.unban(user=target, reason=reason)
        except (NotFound, Forbidden, HTTPException) as e:
            if e == NotFound:
                return True, ""
            return False, "I do not have permission to unban this user."

        if await config.guild(moderation.guild).dm_users() is True:
            try:
                embed = await resolve_factory(
                    moderation=moderation,
                    reason=reason
                )
                await target.send(embed=embed, file=get_icon(bot=moderation.bot))
            except (Forbidden, HTTPException):
                pass
        return True, ""

class Tempban(Ban):
    key="tempban"
    string="tempban"
    verb="tempbanned"
    removes_from_guild=True

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, duration: str, reason: str = None, delete_messages: app_commands.Choice | None = None) -> 'Ban': # pylint: disable=arguments-renamed
        """Ban a user."""
        bot = ctx.bot
        try:
            await ctx.guild.fetch_ban(target)
            await ctx.send(content=error(f"{target.mention} is already {Ban.verb}!"), ephemeral=True)
            return
        except NotFound:
            pass

        if delete_messages is None:
            delete_messages_seconds = 0
        else:
            delete_messages_seconds = delete_messages.value

        try:
            parsed_time = parse_relativedelta(argument=duration)
            if parsed_time is None:
                raise commands.BadArgument()
            parsed_time = timedelta_from_relativedelta(relativedelta=parsed_time)
        except (commands.BadArgument, ValueError):
            await ctx.send(content=error(text="Please provide a valid duration!"), ephemeral=True)
            return cls()

        response_message = await ctx.send(content=f"{target.mention} has been {cls.verb} for {humanize_timedelta(timedelta=parsed_time)}!\n{bold(text='Reason:')} {inline(text=reason)}")

        if silent is False:
            try:
                embed = await message_factory(
                    bot=bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    reason=reason,
                    moderation_type=cls(),
                    moderator=ctx.author,
                    duration=parsed_time,
                    response=response_message
                )
                await target.send(embed=embed, file=get_icon(bot))
            except HTTPException:
                pass

        await ctx.guild.ban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason} (Duration: {parsed_time})", delete_message_seconds=delete_messages_seconds)
        moderation = await Moderation.log(
            bot=bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type='user',
            target_id=target.id,
            role_id=None,
            duration=parsed_time,
            reason=reason
        )
        await response_message.edit(content=f"{target.mention} has been {cls.verb} for {humanize_timedelta(timedelta=parsed_time)}! (Case {inline(text=f'#{moderation.id}')})\n{bold(text='Reason:')} {inline(reason)}")
        await log(ctx, moderation.id)
        await send_evidenceformat(ctx, moderation.id)
        return cls()

    @classmethod
    async def expiry_handler(cls, moderation: Moderation) -> int:
        reason = f"Automatic {Unban.string} from case #{moderation.id}"
        try:
            target = moderation.bot.get_user(moderation.target_id)
            if not target:
                try:
                    target = await moderation.bot.fetch_user(moderation.target_id)
                except NotFound:
                    return 0
            await moderation.guild.unban(user=target, reason=reason)

            if await config.guild(moderation.guild).dm_users() is True:
                try:
                    embed = await message_factory(
                        bot=moderation.bot,
                        color=await moderation.bot.get_embed_color(moderation.guild.channels[0]),
                        guild=moderation.guild,
                        reason=reason,
                        moderation_type=type_registry["unban"],
                        moderator=None,
                        duration=None,
                        response=None,
                        case=False,
                    )
                    await target.send(embed=embed, file=get_icon(bot=moderation.bot))
                except HTTPException:
                    pass
            logger.trace(
                "%s %s (%s) from %s (%s)",
                Unban.verb.title(),
                target.name,
                target.id,
                moderation.guild.name,
                moderation.guild.id,
            )
            return 1
        except (NotFound, Forbidden, HTTPException) as e:
            logger.error(
                "Failed to %s %s (%s) from %s (%s)\n%s",
                Unban.string,
                target.name,
                target.id,
                moderation.guild.name,
                moderation.guild.id,
                e,
            )
            return 0

    @classmethod
    async def duration_edit_handler(cls, interaction: Interaction, old_moderation: Moderation, new_moderation: Moderation) -> bool: # pylint: disable=unused-argument
        return True

class Softban(Type):
    key="softban"
    string="softban"
    verb="softbanned"
    removes_from_guild=True

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str = None, delete_messages: app_commands.Choice | None = None) -> 'Softban':
        """Softban a user."""
        bot = ctx.bot
        try:
            await ctx.guild.fetch_ban(target)
            await ctx.send(content=error(f"{target.mention} is already {Ban.verb}!"), ephemeral=True)
        except NotFound:
            pass

        if delete_messages is None:
            delete_messages_seconds = 0
        else:
            delete_messages_seconds = delete_messages.value

        response_message = await ctx.send(f"{target.mention} has been {cls.verb}!\n{bold('Reason:')} {inline(reason)}")

        if silent is False:
            try:
                embed = await message_factory(
                    bot=bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    reason=reason,
                    moderation_type=cls(),
                    moderator=ctx.author,
                    duration=None,
                    response=response_message
                )
                await target.send(embed=embed, file=get_icon(bot))
            except HTTPException:
                pass

        await ctx.guild.ban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}", delete_message_seconds=delete_messages_seconds)
        await ctx.guild.unban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}")
        moderation = await Moderation.log(
            bot=bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type='user',
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason
        )
        await response_message.edit(content=f"{target.mention} has been {cls.verb}! (Case {inline(f'#{moderation.id}')})\n{bold('Reason:')} {inline(reason)}")
        await log(ctx, moderation.id)
        await send_evidenceformat(ctx, moderation.id)
        return cls()

    @classmethod
    async def resolve_handler(cls, moderation: Moderation, reason: str) -> Tuple[bool, str]:
        if await config.guild(moderation.guild).dm_users() is True:
            try:
                target = await moderation.bot.fetch_user(moderation.target_id)
                embed = await resolve_factory(
                    moderation=moderation,
                    reason=reason
                )
                await target.send(embed=embed, file=get_icon(bot=moderation.bot))
            except (Forbidden, HTTPException, NotFound):
                pass
        return True, ""

class Unban(Type):
    key="unban"
    string="unban"
    verb="unbanned"
    removes_from_guild=True

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str = None) -> 'Unban':
        """Unban a user."""
        bot = ctx.bot
        try:
            await ctx.guild.fetch_ban(target)
        except NotFound:
            await ctx.send(content=error(f"{target.mention} is not {Ban.verb}!"), ephemeral=True)
            return

        response_message = await ctx.send(f"{target.mention} has been {cls.verb}!\n{bold('Reason:')} {inline(reason)}")

        if silent is False:
            try:
                embed = await message_factory(
                    bot=bot,
                    color=await ctx.embed_color(),
                    guild=ctx.guild,
                    reason=reason,
                    moderation_type=cls(),
                    moderator=ctx.author,
                    duration=None,
                    response=response_message
                )
                await target.send(embed=embed, file=get_icon(bot))
            except HTTPException:
                pass

        await ctx.guild.unban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}")
        moderation = await Moderation.log(
            bot=bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type='user',
            target_id=target.id,
            role_id=None,
            duration=None,
            reason=reason
        )
        await response_message.edit(content=f"{target.mention} has been {cls.verb}! (Case {inline(f'#{moderation.id}')})\n{bold('Reason:')} {inline(reason)}")
        await log(ctx, moderation.id)
        await send_evidenceformat(ctx, moderation.id)
        return cls()

class Slowmode(Type):
    key="slowmode"
    string="slowmode"
    verb="set the slowmode in"
    channel=True

    def void(self) -> None:
        return None

    @classmethod
    async def handler(cls, ctx: commands.Context, target: Messageable, silent: bool, duration: str, reason: str) -> 'Slowmode': # pylint: disable=unused-argument
        """Set the slowmode in a channel."""
        bot = ctx.bot
        try:
            parsed_time = parse_relativedelta(argument=duration)
            if parsed_time is None:
                raise commands.BadArgument()
            parsed_time = timedelta_from_relativedelta(relativedelta=parsed_time)
        except (commands.BadArgument, ValueError):
            await ctx.send(content=error(text="Please provide a valid duration!"), ephemeral=True)
            return cls()

        if ceil(parsed_time.total_seconds()) > 21600:
            await ctx.send(content=error(text="The slowmode duration cannot exceed 6 hours!"), ephemeral=True)
            return cls()

        if isinstance(target, TextChannel):
            await target.edit(slowmode_delay=ceil(parsed_time.total_seconds()))
        moderation = await Moderation.log(
            bot=bot,
            guild_id=ctx.guild.id,
            moderator_id=ctx.author.id,
            moderation_type=cls(),
            target_type='channel',
            target_id=target.id,
            role_id=None,
            duration=parsed_time,
            reason=None
        )
        await ctx.send(content=f"{ctx.author.mention} has {cls.verb} {target.mention} to {humanize_timedelta(timedelta=parsed_time)}!\n{bold(text='Reason:')} {inline(text=reason)}")
        await log(ctx=ctx, moderation_id=moderation.id)
        return cls()

class Lockdown(Type):
    key="lockdown"
    string="lockdown"
    verb="locked down"
    channel=True

    def void(self) -> None:
        return None
