from __future__ import annotations

from typing import TYPE_CHECKING, Optional

import discord
import pendulum
from discord.ext import commands

from ..utils.common import ArtemisError, parse_short_time

if TYPE_CHECKING:
    from ..bot import Artemis


class ShortTime(commands.Converter):
    async def convert(self, ctx: commands.Context, argument: str) -> pendulum.DateTime:
        return parse_short_time(argument)


class Mod(commands.Cog):
    def __init__(self, bot: Artemis):
        self.bot: Artemis = bot

    def cog_check(self, ctx: commands.Context):
        if ctx.guild:
            return True
        raise commands.CheckFailure("This command cannot be used in private messages.")

    @commands.command()
    @commands.has_permissions(kick_members=True)
    async def kick(self, ctx: commands.Context, member: discord.Member, *, reason: Optional[str]):
        """Kicks a member with an optional reason."""
        if not reason:
            reason = f"Action done by {ctx.author} ({ctx.author.id})"
        else:
            reason = f"{ctx.author} ({ctx.author.id}): {reason}"
        await ctx.guild.kick(member, reason=reason)
        await ctx.reply(f"Successfully kicked {member}.")

    @commands.command()
    @commands.has_permissions(ban_members=True)
    async def ban(self, ctx: commands.Context, member: discord.Member, *, reason: Optional[str]):
        """Bans a member with an optional reason."""
        if not reason:
            reason = f"Action done by {ctx.author} ({ctx.author.id})"
        else:
            reason = f"{ctx.author} ({ctx.author.id}): {reason}"
        await ctx.guild.ban(member, reason=reason)
        await ctx.reply(f"Successfully banned {member}.")

    @commands.command()
    @commands.has_permissions(moderate_members=True)
    async def mute(
        self,
        ctx: commands.Context,
        member: discord.Member,
        time: ShortTime,
        *,
        reason: Optional[str],
    ):
        """
        Mutes a member for a specific time with an optional reason.

        Usage examples:
        `{prefix}mute 555412947883524098 2h`
        `{prefix}mute Artemis 12h`
        `{prefix}mute Artemis 2d12h for being nosy`
        """
        max_timeout = pendulum.now("UTC").add(days=28)
        if time > max_timeout:
            raise ArtemisError("Mute time cannot exceed 28 days.")

        if not reason:
            reason = f"Action done by {ctx.author} ({ctx.author.id})"
        else:
            reason = f"{ctx.author} ({ctx.author.id}): {reason}"

        await member.timeout(time, reason=reason)
        return await ctx.reply(
            f"Successfully muted {member} until {discord.utils.format_dt(time)}."
        )

    @commands.command()
    @commands.has_permissions(moderate_members=True)
    async def unmute(self, ctx: commands.Context, member: discord.Member):
        """
        Unmutes a member.
        """
        if not member.timed_out_until:
            return await ctx.reply("This member is not muted.")

        await member.timeout(None, reason=f"Action done by {ctx.author} ({ctx.author.id})")
        return await ctx.reply(f"Successfully unmuted {member}.")

    async def move_impl(
        self,
        ctx: commands.Context,
        channel: discord.TextChannel,
        *,
        no_messages: int = None,
        from_message_id: int = None,
        to_message_id: int = None,
        reason: str = None,
    ):
        await ctx.typing()

        webhooks = await channel.webhooks()
        webhook = discord.utils.get(webhooks, name="Artemis")

        if not webhook:
            webhook = await channel.create_webhook(
                name="Artemis", reason="Creating general-purpose webhook (called from $move)."
            )

        if no_messages:
            messages = [msg async for msg in ctx.history(limit=no_messages + 1)]
            messages = messages[::-1][:-1]
        else:
            messages = [
                msg
                async for msg in ctx.history(
                    after=discord.Object(from_message_id - 1),
                    before=discord.Object(to_message_id + 1),
                )
            ]

        for message in messages:
            embeds = []
            files = [await attachment.to_file() for attachment in message.attachments]

            if not message.content and message.author.bot:
                embeds = [embed for embed in message.embeds if embed.type == "rich"]

            if not message.content and not files and not embeds:
                continue

            await webhook.send(
                content=message.content,
                username=message.author.display_name,
                avatar_url=message.author.avatar.url,
                files=files,
                embeds=embeds,
            )
            await message.delete()

        await ctx.message.delete()

        if reason:
            await channel.send(
                f"Moved **{len(messages)}** messages from {ctx.channel.mention} for: `{reason}`",
                delete_after=30,
            )
        else:
            await channel.send(
                f"Moved **{len(messages)}** messages from {ctx.channel.mention}.", delete_after=30
            )

    @commands.command()
    @commands.has_permissions(manage_messages=True)
    async def move(
        self,
        ctx: commands.Context,
        channel: discord.TextChannel,
        no_messages: int,
        *,
        reason: Optional[str],
    ):
        """Move `no_messages` to `channel` with an optional `reason`."""
        await self.move_impl(ctx, channel, no_messages=no_messages, reason=reason)

    @commands.command()
    @commands.has_permissions(manage_messages=True)
    async def move2(
        self,
        ctx: commands.Context,
        channel: discord.TextChannel,
        from_message_id: int,
        to_message_id: int,
        *,
        reason: Optional[str],
    ):
        """Move `from_message_id`-`to_message_id` messages to `channel` with an optional `reason`."""
        await self.move_impl(
            ctx,
            channel,
            from_message_id=from_message_id,
            to_message_id=to_message_id,
            reason=reason,
        )


async def setup(bot: Artemis):
    await bot.add_cog(Mod(bot))
