# mautrix-telegram - A Matrix-Telegram puppeting bridge
# Copyright (C) 2019 Tulir Asokan
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
from typing import Dict, Callable, Optional

from mautrix.types import RoomID, EventID

from ... import portal as po
from .. import command_handler, CommandEvent, SECTION_PORTAL_MANAGEMENT
from .util import user_has_power_level


async def _get_portal_and_check_permission(evt: CommandEvent, permission: str,
                                           action: Optional[str] = None
                                           ) -> Optional[po.Portal]:
    room_id = RoomID(evt.args[0]) if len(evt.args) > 0 else evt.room_id

    portal = po.Portal.get_by_mxid(room_id)
    if not portal:
        that_this = "This" if room_id == evt.room_id else "That"
        await evt.reply(f"{that_this} is not a portal room.")
        return None

    if not await user_has_power_level(portal.mxid, evt.az.intent, evt.sender, permission):
        action = action or f"{permission.replace('_', ' ')}s"
        await evt.reply(f"You do not have the permissions to {action} that portal.")
        return None
    return portal


def _get_portal_murder_function(action: str, room_id: str, function: Callable, command: str,
                                completed_message: str) -> Dict:
    async def post_confirm(confirm) -> Optional[EventID]:
        confirm.sender.command_status = None
        if len(confirm.args) > 0 and confirm.args[0] == f"confirm-{command}":
            await function()
            if confirm.room_id != room_id:
                return await confirm.reply(completed_message)
        else:
            return await confirm.reply(f"{action} cancelled.")
        return None

    return {
        "next": post_confirm,
        "action": action,
    }


@command_handler(needs_auth=False, needs_puppeting=False,
                 help_section=SECTION_PORTAL_MANAGEMENT,
                 help_text="Remove all users from the current portal room and forget the portal. "
                           "Only works for group chats; to delete a private chat portal, simply "
                           "leave the room.")
async def delete_portal(evt: CommandEvent) -> Optional[EventID]:
    portal = await _get_portal_and_check_permission(evt, "unbridge")
    if not portal:
        return None

    evt.sender.command_status = _get_portal_murder_function("Portal deletion", portal.mxid,
                                                            portal.cleanup_and_delete, "delete",
                                                            "Portal successfully deleted.")
    return await evt.reply("Please confirm deletion of portal "
                           f"[{portal.alias or portal.mxid}](https://matrix.to/#/{portal.mxid}) "
                           f"to Telegram chat \"{portal.title}\" "
                           "by typing `$cmdprefix+sp confirm-delete`"
                           "\n\n"
                           "**WARNING:** If the bridge bot has the power level to do so, **this "
                           "will kick ALL users** in the room. If you just want to remove the "
                           "bridge, use `$cmdprefix+sp unbridge` instead.")


@command_handler(needs_auth=False, needs_puppeting=False,
                 help_section=SECTION_PORTAL_MANAGEMENT,
                 help_text="Remove puppets from the current portal room and forget the portal.")
async def unbridge(evt: CommandEvent) -> Optional[EventID]:
    portal = await _get_portal_and_check_permission(evt, "unbridge")
    if not portal:
        return None

    evt.sender.command_status = _get_portal_murder_function("Room unbridging", portal.mxid,
                                                            portal.unbridge, "unbridge",
                                                            "Room successfully unbridged.")
    return await evt.reply(f"Please confirm unbridging chat \"{portal.title}\" from room "
                           f"[{portal.alias or portal.mxid}](https://matrix.to/#/{portal.mxid}) "
                           "by typing `$cmdprefix+sp confirm-unbridge`")
