import datetime
import random
from datetime import datetime
from textwrap import wrap
from typing import Union

import discord
from discord.ext import commands

from lib.emotes import basic_emoji


class Miscellaneous(commands.Cog):
    """Other interesting commands"""

    def __init__(self, bot):
        self.bot = bot

    @commands.command(name="ping", help="Display bot's ping.")
    async def ping(self, ctx):
        """Displays time delta between Discord message and command invocation"""

        ms = (datetime.utcnow() - ctx.message.created_at).total_seconds() * 1000
        await ctx.send(basic_emoji.get("Pepega") + " 🏓 Pong! `{0}ms`".format(int(ms)))

    @commands.command(name="roll", help="Generate a random number between 1 and 100 by default.")
    async def roll(self, ctx, num: str = "100"):
        """Roll a dice"""

        # Default string for invalid input
        result = "No, I don't think so. " + basic_emoji.get("forsenSmug")

        # Parse input
        if num.isnumeric():
            # Roll dice
            result = str(random.randint(1, int(num)))
        else:
            await ctx.message.add_reaction(basic_emoji.get("Si"))

        # Display result
        await ctx.send(result)

    @commands.command(name="decide", aliases=["choose"], help="Decide between options.")
    async def decide(self, ctx, *args):
        """Choose one option from a list"""

        # No arguments -> exit
        if not args:
            await ctx.send("Decide between what? " + basic_emoji.get("Pepega") + basic_emoji.get("Clap") + "\nUse `;`, `:`, `,` or ` or `, to separate options.")
            await ctx.message.add_reaction(basic_emoji.get("Si"))
            return

        # Join arguments to one string
        raw = " ".join(str(i) for i in args)

        # Attempt to split it by any separator
        options = raw.split(";")
        if len(options) < 2:
            options = raw.split(":")
            if len(options) < 2:
                options = raw.split(",")
                if len(options) < 2:
                    options = raw.split(" or ")

        # Splitting failed
        if len(options) < 2:
            await ctx.send("Separator not recognized, use `;`, `:`, `,` or ` or `, to separate options.")

        # Else send a result
        else:
            await ctx.send(random.choice(options))

    @commands.command(name="created", help="Find when an account was created")
    async def created(self, ctx, user: Union[discord.Member, discord.User, discord.ClientUser, str, None]):
        """Display account creation date"""

        if isinstance(user, str):
            await ctx.send("Try a user's tag instead " + basic_emoji.get("Okayga"))
            return

        if user is None:
            user_id = ctx.author.id
            msg = "Your account"
        else:
            user_id = user.id
            msg = "That account"

        # Decode user's ID
        binary = str(bin(user_id)[2:])
        unix_binary = binary[:len(binary) - 22]
        unix = (int(unix_binary, 2) + 1420070400000) // 1000

        time = datetime.utcfromtimestamp(unix).strftime("%Y-%m-%d %H:%M:%S")

        await ctx.send("{0} was created at {1} UTC".format(msg, time))

    @commands.command(name="assemble", aliases=["ass"], help="Tag online/idle users with role")
    @commands.guild_only()
    async def assemble(self, ctx, role_name: Union[discord.role.Role, str, None]):
        """Attempt to find role, then tag online/idle users with that role"""

        if not role_name:
            await ctx.send("No role name provided.")
            return

        if type(role_name) is discord.role.Role:
            role = role_name
        else:
            role = next((role for role in ctx.guild.roles if role.name == role_name), None)

        if not role:
            await ctx.send("No role with this name exists.")
            return
        if not role.members:
            await ctx.send("No users with this role exist.")
            return

        online_members = list()
        for member in role.members:
            if member.status in [discord.Status.online, discord.Status.idle]:
                online_members.append(member)
        if not online_members:
            await ctx.send("No users with this role are currently online.")
            return

        tag = " ".join(member.mention for member in online_members)
        # This split likely mangles any tag on the edge, oh well
        for tag_part in wrap(tag, 2000):
            await ctx.send(tag_part)

def setup(bot):
    bot.add_cog(Miscellaneous(bot))
