from redbot.core import commands
from redbot.core.config import Config
from .resolvers import entries_from_ctx, resolve_lists

# This has optimizations in it that may not hold True if other parts of the permission
# model are changed from the state they are in currently.
# (commit hash ~ 3bcf375204c22271ad3ed1fc059b598b751aa03f)
#
# This is primarily to help with the performance of the help formatter

# This is less efficient if only checking one command,
# but is much faster for checking all of them.


async def mass_resolve(*, ctx: commands.Context, config: Config):
    """
    Get's all the permission cog interactions for all loaded commands
    in the given context.
    """

    owner_settings = await config.owner_models()
    guild_owner_settings = await config.guild(ctx.guild).owner_models() if ctx.guild else None

    ret = {"allowed": [], "denied": [], "default": []}

    for cogname, cog in ctx.bot.cogs.items():

        cog_setting = resolve_cog_or_command(
            objname=cogname, models=owner_settings, ctx=ctx, typ="cogs"
        )
        if cog_setting is None and guild_owner_settings:
            cog_setting = resolve_cog_or_command(
                objname=cogname, models=guild_owner_settings, ctx=ctx, typ="cogs"
            )

        for command in [c for c in ctx.bot.all_commands.values() if c.instance is cog]:
            resolution = recursively_resolve(
                com_or_group=command,
                o_models=owner_settings,
                g_models=guild_owner_settings,
                ctx=ctx,
            )

            for com, resolved in resolution:
                if resolved is None:
                    resolved = cog_setting
                if resolved is True:
                    ret["allowed"].append(com)
                elif resolved is False:
                    ret["denied"].append(com)
                else:
                    ret["default"].append(com)

    ret = {k: set(v) for k, v in ret.items()}

    return ret


def recursively_resolve(*, com_or_group, o_models, g_models, ctx, override=False):
    ret = []
    if override:
        current = False
    else:
        current = resolve_cog_or_command(
            typ="commands", objname=com_or_group.qualified_name, ctx=ctx, models=o_models
        )
        if current is None and g_models:
            current = resolve_cog_or_command(
                typ="commands", objname=com_or_group.qualified_name, ctx=ctx, models=o_models
            )
    ret.append((com_or_group, current))
    if isinstance(com_or_group, commands.Group):
        for com in com_or_group.commands:
            ret.extend(
                recursively_resolve(
                    com_or_group=com,
                    o_models=o_models,
                    g_models=g_models,
                    ctx=ctx,
                    override=(current is False),
                )
            )
    return ret


def resolve_cog_or_command(*, typ, ctx, objname, models: dict) -> bool:
    """
    Resolves models in order.
    """

    resolved = None

    if objname in models.get(typ, {}):
        blacklist = models[typ][objname].get("deny", [])
        whitelist = models[typ][objname].get("allow", [])
        resolved = resolve_lists(ctx=ctx, whitelist=whitelist, blacklist=blacklist)
        if resolved is not None:
            return resolved
        resolved = models[typ][objname].get("default", None)
        if resolved is not None:
            return resolved
    return None
