import re

import aiohttp
import disnake

import arguments
import commands
import utils
from constants import APPLICATION_FLAGS, BADGE_EMOJIS, EMBED_COLOR, PUBLIC_FLAGS
from state import client


async def lookup(message):
    tokens = commands.tokenize(message.content)
    parser = arguments.ArgumentParser(
        tokens[0],
        "look up a discord user or application by ID",
    )
    parser.add_argument(
        "-a",
        "--application",
        action="store_true",
        help="look up applications instead of users",
    )
    parser.add_argument(
        "id",
        type=int,
        help="the ID to perform a search for",
    )
    if not (args := await parser.parse_args(message, tokens)):
        return

    if args.application:
        session = aiohttp.ClientSession()
        response = await (
            await session.get(f"https://discord.com/api/v9/applications/{args.id}/rpc")
        ).json()
        if "code" in response.keys():
            await utils.reply(message, "application not found!")
            return

        embed = disnake.Embed(description=response["description"], color=EMBED_COLOR)
        embed.set_thumbnail(
            url=f"https://cdn.discordapp.com/app-icons/{response['id']}/{response['icon']}.webp",
        )
        embed.add_field(name="Application Name", value=response["name"])
        embed.add_field(name="Application ID", value="`" + response["id"] + "`")
        embed.add_field(
            name="Public Bot",
            value=f"{'`' + str(response['bot_public']) + '`' if 'bot_public' in response else 'No bot'}",
        )
        embed.add_field(name="Public Flags", value="`" + str(response["flags"]) + "`")
        embed.add_field(
            name="Terms of Service",
            value=(
                "None"
                if "terms_of_service_url" not in response.keys()
                else f"[Link]({response['terms_of_service_url']})"
            ),
        )
        embed.add_field(
            name="Privacy Policy",
            value=(
                "None"
                if "privacy_policy_url" not in response.keys()
                else f"[Link]({response['privacy_policy_url']})"
            ),
        )
        embed.add_field(
            name="Creation Time",
            value=f"<t:{utils.snowflake_timestamp(int(response['id']))}:R>",
        )
        embed.add_field(
            name="Default Invite URL",
            value=(
                "None"
                if "install_params" not in response.keys()
                else f"[Link](https://discord.com/oauth2/authorize?client_id={response['id']}&permissions={response['install_params']['permissions']}&scope={'%20'.join(response['install_params']['scopes'])})"
            ),
        )
        embed.add_field(
            name="Custom Invite URL",
            value=(
                "None"
                if "custom_install_url" not in response.keys()
                else f"[Link]({response['custom_install_url']})"
            ),
        )

        bot_intents = []
        for application_flag, intent_name in APPLICATION_FLAGS.items():
            if response["flags"] & application_flag == application_flag:
                if intent_name.replace(" (unverified)", "") not in bot_intents:
                    bot_intents.append(intent_name)
        embed.add_field(
            name="Application Flags",
            value=", ".join(bot_intents) if bot_intents else "None",
        )

        bot_tags = ""
        if "tags" in response.keys():
            for tag in response["tags"]:
                bot_tags += tag + ", "
        embed.add_field(
            name="Tags",
            value="None" if bot_tags == "" else bot_tags[:-2],
            inline=False,
        )
    else:
        try:
            user = await client.fetch_user(args.id)
        except Exception:
            await utils.reply(message, "user not found!")
            return

        badges = ""
        for flag, flag_name in PUBLIC_FLAGS.items():
            if user.public_flags.value & flag == flag:
                if flag_name != "None":
                    try:
                        badges += BADGE_EMOJIS[PUBLIC_FLAGS[flag]]
                    except Exception:
                        raise Exception(f"unable to find badge: {PUBLIC_FLAGS[flag]}")

        user_object = await client.fetch_user(user.id)
        accent_color = 0x000000
        if user_object.accent_color is not None:
            accent_color = user_object.accent_color

        embed = disnake.Embed(color=accent_color)
        embed.add_field(
            name="User ID",
            value=f"`{user.id}`",
        )
        embed.add_field(
            name="Discriminator",
            value=f"`{user.name}#{user.discriminator}`",
        )
        embed.add_field(
            name="Creation Time",
            value=f"<t:{utils.snowflake_timestamp(int(user.id))}:R>",
        )
        embed.add_field(
            name="Public Flags",
            value=f"`{user.public_flags.value}` {badges}",
        )
        embed.add_field(
            name="Bot User",
            value=f"`{user.bot}`",
        )
        embed.add_field(
            name="System User",
            value=f"`{user.system}`",
        )
        embed.set_thumbnail(url=user.avatar if user.avatar else user.default_avatar)
        if user_object.banner:
            embed.set_image(url=user_object.banner)

    await utils.reply(message, embed=embed)


async def clear(message):
    tokens = commands.tokenize(message.content)
    parser = arguments.ArgumentParser(
        tokens[0],
        "bulk delete messages in the current channel matching certain criteria",
    )
    parser.add_argument(
        "count",
        type=lambda c: arguments.range_type(c, lower=1, upper=1000),
        help="amount of messages to delete",
    )
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        "-r",
        "--regex",
        required=False,
        help="delete messages with content matching this regex",
    )
    group.add_argument(
        "-c",
        "--contains",
        required=False,
        help="delete messages with content containing this substring",
    )
    parser.add_argument(
        "-i",
        "--case-insensitive",
        action="store_true",
        help="ignore case sensitivity when deleting messages",
    )
    parser.add_argument(
        "-a",
        "--author-id",
        type=int,
        action="append",
        help="delete messages whose author matches this id",
    )
    parser.add_argument(
        "-o",
        "--oldest-first",
        action="store_true",
        help="delete oldest messages first",
    )
    parser.add_argument(
        "-R",
        "--reactions",
        action="store_true",
        help="delete messages with reactions",
    )
    parser.add_argument(
        "-A",
        "--attachments",
        action="store_true",
        help="delete messages with attachments",
    )
    parser.add_argument(
        "-d",
        "--delete-command",
        action="store_true",
        help="delete the command message as well",
    )
    parser.add_argument(
        "-I",
        "--ignore-ids",
        type=int,
        action="append",
        help="ignore messages with this id",
    )
    if not (args := await parser.parse_args(message, tokens)):
        return

    if args.delete_command:
        try:
            await message.delete()
        except Exception:
            pass

    regex = None
    if r := args.regex:
        regex = re.compile(r, re.IGNORECASE if args.case_insensitive else 0)

    def check(m):
        if (ids := args.ignore_ids) and m.id in ids:
            return False
        c = []
        if regex:
            c.append(regex.search(m.content))
        if s := args.contains:
            if args.case_insensitive:
                c.append(s.lower() in m.content.lower())
            else:
                c.append(s in m.content)
        if i := args.author_id:
            c.append(m.author.id in i)
        if args.reactions:
            c.append(len(m.reactions) > 0)
        if args.attachments:
            c.append(len(m.attachments) > 0)
        return all(c)

    messages = len(
        await message.channel.purge(
            limit=args.count,
            check=check,
            oldest_first=args.oldest_first,
        ),
    )

    if not args.delete_command:
        try:
            await utils.reply(
                message,
                f"purged **{messages}/{args.count} {'message' if args.count == 1 else 'messages'}**",
            )
        except Exception:
            pass