"""
This plugin allows sending one or more emoji reactions to other messages.

It requires ``python-emoji`` to work properly (aliases handling, emoji
validation, etc).

Command
-------

.. glossary::

    /react
        **Usage in a chat tab:** ``/react <message time> [emoji] [emoji]…``

Options
-------

.. glossary::

    complete_aliases

        **Default value:** ``false``

        If true, do not autocomplete emojis based on their real value, but
        instead use their textual names or aliases (e.g. ``:sob:`` instead of
        ``😭``).
"""

import logging

from functools import lru_cache

from poezio import common
from poezio.decorators import command_args_parser
from poezio.plugin import BasePlugin
from poezio.core.structs import Completion
from poezio.ui.types import Message
from poezio.windows.inputs import Input
from poezio import tabs
from typing import Literal
try:
    import emoji
except ImportError:
    emoji = None  # type: ignore


log = logging.getLogger(__name__)

DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S'

if emoji is None:
    log.warning(
        'python-emoji was not found, some reaction features '
        'will not be available'
    )


@lru_cache(50)
def demojize(string: str) -> str:
    """Convert an emoji to a string representation"""
    if emoji is None:
        return string
    return emoji.demojize(string)


@lru_cache(50)
def translate_str_to_emoji(string: str) -> str:
    """
    Convert a string representation to an emoji.
    Handles both normal names and aliases.
    Idempotent in all other cases.
    """
    if emoji is None:
        return string

    new_string = emoji.emojize(string)
    if new_string != string:
        return new_string
    return emoji.emojize(string, language='alias')


class Plugin(BasePlugin):
    """Message Reactions (XEP-0444) plugin"""

    default_config = {
        'default': {
            'complete_aliases': False,
        },
    }

    def init(self) -> None:
        # All chat tabs
        for _tabtype in (tabs.MucTab, tabs.DynamicConversationTab,
                         tabs.StaticConversationTab, tabs.PrivateTab):
            self.api.add_tab_command(
                _tabtype,
                name='react',
                handler=self.command_react,
                usage='<message time> [emojis]',
                help='React to a message using its timestamp (using tab complete)',
                short='React to a message',
                completion=self.completion_react,
            )

    def completion_react(self, the_input: Input) -> Completion | None:
        """Datetime and emoji completion"""
        is_muc = isinstance(self.api.current_tab(), tabs.MucTab)
        pos = the_input.get_argument_position()
        # First position: we want to complete the message timestamp
        if pos == 1:
            all_messages = self.api.get_conversation_messages()[:-100:-1]
            if is_muc:
                filtered_messages = filter(
                    lambda m: isinstance(m, Message) and m.nickname and m.stanza_id,
                    all_messages,
                )
            else:
                filtered_messages = filter(
                    lambda m: isinstance(m, Message),
                    all_messages,
                )
            messages = list(map(lambda m: m.time.strftime(DATETIME_FORMAT), filtered_messages))

            if messages:
                return Completion(the_input.auto_completion, messages, quotify=False)
        # Second position: iterate through the already received emojis
        # for this message.
        elif pos >= 2:
            complete_aliases = self.config.getbool('complete_aliases')
            time = common.shell_split(the_input.text)[1]
            found = self.api.find_message(time, DATETIME_FORMAT)
            if found and found.reactions:
                if complete_aliases:
                    to_complete = [
                        demojize(reaction) for reaction in found.reactions
                    ]
                else:
                    to_complete = list(found.reactions)
                return Completion(
                    the_input.auto_completion,
                    to_complete,
                    quotify=False,
                )
        return None

    @command_args_parser.quoted(1, 1)
    def command_react(self, args: list[str]) -> None:
        """React to a message"""
        if not args:
            return
        tab = self.api.current_tab()
        is_muc = isinstance(tab, tabs.MucTab)
        if not isinstance(tab, tabs.ChatTab):
            return

        emojis = ""
        time = args[0]
        if len(args) > 1:
            emojis = args[1]
        emoji_set = set(
            translate_str_to_emoji(emoji)
            for emoji in emojis.split() if emoji
        )

        found_msg = self.api.find_message(time, DATETIME_FORMAT)
        if not found_msg:
            self.api.information(
                f'Message with timestamp “{time}” not found.',
                'Error',
            )
            return

        mtype: Literal['chat', 'groupchat'] = 'chat'
        id_ = found_msg.identifier
        # in MUCs, use stanza-ids and "groupchat" message type
        if is_muc:
            mtype = 'groupchat'
            id_ = found_msg.stanza_id or ''
            if not id_:
                self.api.information(
                    f'Message with timestamp “{time}” '
                    'found but contained no “{urn:xmpp:sid:0}stanza-id” identifier.',
                    'Error',
                )
                return

        # We need the mfrom specifically here because we need to handle this
        # message ourselves.
        msg = self.core.xmpp.make_message(
            mtype=mtype, mto=tab.jid,
            mfrom=self.core.xmpp.boundjid,
        )
        try:
            self.core.xmpp['xep_0444'].set_reactions(msg, id_, emoji_set)
        except ValueError as exc:
            self.api.information(f'Failed setting reactions: {exc.args[0]}', 'Error')
        else:
            msg.send()
            if isinstance(tab, tabs.OneToOneTab):
                tab.handle_reactions(msg)
