# Mattermost Bot.
#  Copyright (c) 2016-2022 by Someone <someone@somenet.org> (aka. Jan Vales <jan@jvales.net>)
#  published under MIT-License


import mattermost

class RequireException(Exception):
    """Required precondition not met-exception. Mostly used for permission checks. Message will be forwarded to command-caller/user"""


class AbstractCommand():
    ICON_PUBLIC = "📢 "
    ICON_DM = "👥 "
    ICON_PRIVATE = "🥷 "

    URL = None
    TEAM_ID = None
    TRIGGER = None
    CONFIG = {"display_name": "somebot-command", "auto_complete": True}
    USEINFO = None

    bot = None
    mm_secret_token = None


    def __init__(self, team_id):
        self.TEAM_ID = team_id

    def __str__(self):
        return str(self.__class__)+" for team_id: "+str(self.TEAM_ID)

    def __repr__(self):
        return self.__str__()


    # can/should be overridden by the user
    def on_register(self):
        """Consider to override. Handles the post-command-registration logic at bot startup."""
        self._create_slash_command()

    # can/should be overridden by the user
    def on_shutdown(self):
        """Consider to override. Handles the shutdown-procedure."""
        return

    # can/should be overridden by the user
    def on_SIGUSR1(self, sigusr1_cnt):
        """Consider to override. Handles the SIGUSR1-signal."""
        return

    # should be overridden by the user
    def on_POST(self, request, data):
        """Override. Handles the post-command logic."""
        return

    # should be overridden by the user
    # manual command authentication needed!
    def on_POST_interactive(self, request, data):
        """Consider to override. Handles the interactive-message logic."""
        return

    # should be overridden by the user
    # manual command authentication needed!
    def on_POST_dialog(self, request, data):
        """Consider to override. Handles the dialog logic."""
        return


    def _on_register(self, bot):
        self.bot = bot
        self.URL = ("http://"+self.bot.local_websrv_hostname+":"+str(self.bot.local_websrv_port)+"/").strip("/")+"/"+self.TEAM_ID+"/"+self.TRIGGER
        if self.USEINFO:
            self.bot.USETOPICS.setdefault("/"+self.TRIGGER, self.USEINFO)
        self.on_register()


    def _on_shutdown(self):
        self.on_shutdown()


    def _on_SIGUSR1(self, sigusr1_cnt):
        self.on_SIGUSR1(sigusr1_cnt)


    def _on_POST(self, request, data):
        try:
            self._require_not_guest(data)
            self.on_POST(request, data)
        except RequireException as ex:
            request.respond_cmd_err(str(ex))


    def _on_POST_interactive(self, request, data):
        try:
            self._require_not_guest(data)
            self.on_POST_interactive(request, data)
        except RequireException as ex:
            request.respond_cmd_err(str(ex))


    def _on_POST_dialog(self, request, data):
        try:
            self._require_not_guest(data)
            self.on_POST_dialog(request, data)
        except RequireException as ex:
            request.respond_cmd_err(str(ex))


    def _create_slash_command(self):
        # (possibly) delete old version of command
        for command in self.bot.api.list_custom_slash_commands_for_team(self.TEAM_ID):
            if command["url"] == self.URL or command["trigger"].lower() == self.TRIGGER.lower():
                self.bot.api.delete_slash_command(command["id"])

        # create slash command
        res = self.bot.api.create_slash_command(self.TEAM_ID, self.TRIGGER.lower(), self.URL+"/command")
        res.update(self.CONFIG)
        self.bot.api.update_slash_command(res)
        self.mm_secret_token = res["token"]


    def _require_bot_admin(self, data):
        """
        Require exactly bot admin priviledges.
        Throws RequireException if not priviledged.
        """
        if not data["user_id"] in self.bot.admin_ids:
            raise RequireException("### Leave me alone. You are not by real dad.")


    def _require_system_admin(self, data, exc=True):
        """
        Require at least team admin priviledges.
        Throws RequireException if not priviledged.
        """
        user = self.bot.api.get_user(data["user_id"])
        if "system_admin" not in user["roles"]:
            if exc:
                raise RequireException("### You are not SYSTEM_ADMIN. :(")
            return False
        return True


    def _require_team_admin(self, data, exc=True):
        """
        Require at least team admin priviledges.
        Throws RequireException if not priviledged.
        """
        team_member = self.bot.api.get_team_member(data["team_id"], data["user_id"])
        if "team_admin" not in team_member["roles"] and not self._require_system_admin(data, exc=False):
            if exc:
                raise RequireException("### You are not TEAM_ADMIN. :(")
            return False
        return True


    def _require_channel_admin(self, data, exc=True):
        """
        Require at least channel admin priviledges.
        Throws RequireException if not priviledged.
        """
        channel_member = self.bot.api.get_channel_member(data["channel_id"], data["user_id"])
        if "channel_admin" not in channel_member["roles"] and not self._require_team_admin(data, exc=False):
            if exc:
                raise RequireException("### You are not CHANNEL_ADMIN. :(")
            return False
        return True


    def _require_not_guest(self, data, exc=True):
        """
        Require to not be a guest.
        Throws RequireException if guest.
        """
        channel_member = self.bot.api.get_channel_member(data["channel_id"], data["user_id"])
        if "channel_guest" in channel_member["roles"]:
            if exc:
                raise RequireException("### The bot cannot be used by guests. :(")
            return False
        return True


    def _require_in_channel(self, in_channel, data, exc=True):
        """
        Require to be in a channel.
        Throws RequireException if not in the given channel.
        """

        try:
            channel_member = self.bot.api.get_channel_member(in_channel, data["user_id"], exc=True)
        except mattermost.ApiException as ex:
            if exc:
                raise RequireException("### You are not in the required channel :(")
            return False
        return True
