import re
from datetime import datetime
from typing import TYPE_CHECKING, Awaitable, Callable, TypeVar, Union

from mattermost_api_reference_client.models import FileInfo
from mattermost_api_reference_client.types import Unset
from pyttermost.events import Post, get_slack_attachments
from pyttermost.util import emojize_body
from slidge.core.mixins.message import ContentMessageMixin
from slidge.util.types import LegacyAttachment

if TYPE_CHECKING:
    from .session import Session


class UserMixin(ContentMessageMixin):
    session: "Session"
    mm_id: Callable[[], Awaitable[str]]

    async def send_mm_post(self, post: Post, carbon=False, archive_only=False):
        assert post.metadata
        assert post.update_at

        file_metas = post.metadata.files
        if not (isinstance(file_metas, list)):
            file_metas = []
        if isinstance(m := post.message, str):
            text = emojize_body(m)
        else:
            text = ""
        post_id = post.id

        when = datetime.fromtimestamp(post.update_at / 1000)

        for slack_attachment in get_slack_attachments(post):
            if slack_attachment.fallback:
                text += emojize_body(slack_attachment.fallback) + " (BOT)"

        await self.send_files(
            [
                Attachment.from_mm(x, await self.session.mm_client.api.get_file(x.id))
                for x in file_metas  # type:ignore[var-annotated]
            ],
            post_id,
            body=text,
            when=when,
            carbon=carbon,
            thread=post.root_id or post_id,
            body_first=True,
            archive_only=archive_only,
        )

    async def update_reactions(self, legacy_msg_id: str, carbon: bool = False):
        self.react(
            legacy_msg_id,
            await self.session.get_mm_reactions(
                legacy_msg_id,
                await self.session.mm_client.api.mm_id
                if carbon
                else await self.mm_id(),
            ),
            carbon=carbon,
        )


class Attachment(LegacyAttachment):
    @staticmethod
    def from_mm(info: FileInfo, data: bytes):
        return Attachment(
            name=unset_to_none(info.name),
            legacy_file_id=unset_to_none(info.id),
            # TODO: data could be an awaitable of bytes in slidge core so we don't
            #       have to fetch if legacy_file_id has already been seen
            data=data,
        )


T = TypeVar("T")


def unset_to_none(x: Union[T, Unset]) -> Union[T, None]:
    if isinstance(x, Unset):
        return None
    return x


def ensure_newline_after_quote(text: str) -> str:
    """
    Adds an empty newline after lines prepended by ">" unless an empty newline already exists.

    We need this because without the extra empty newline, mattermost collapse everything in the quote
    """
    return re.sub(_NEW_LINE_AFTER_QUOTE_REGEX, r"\1\n\n", text)


_NEW_LINE_AFTER_QUOTE_REGEX = re.compile(r"(>*>.*)\n(?=[^\n>])", flags=re.MULTILINE)
