import datetime

from conftest import UUIDFixtureMixin
from slixmpp import Message, Presence
from slixmpp.exceptions import XMPPError

from slidge import (
    BaseGateway,
    BaseSession,
    LegacyBookmarks,
    LegacyContact,
    LegacyParticipant,
    LegacyRoster,
)
from slidge.group import LegacyMUC
from slidge.util.test import SlidgeTest


class Session(BaseSession):
    async def login(self):
        pass


class Gateway(BaseGateway[Session]):
    COMPONENT_NAME = "Megacorp Chat XMPP Gateway"
    GROUPS = True


class Roster(LegacyRoster[LegacyContact]):
    async def legacy_id_to_jid_username(self, legacy_id: str) -> str:
        if legacy_id.startswith("room-"):
            raise XMPPError("item-not-found", "This is a MUC JID")
        return legacy_id


class MUC(LegacyMUC[LegacyParticipant]):
    async def update_info(self):
        self.subject = "a subject"
        self.subject_setter = "occ1"
        self.subject_date = datetime.datetime(2000, 1, 1)

    async def fill_participants(self):
        for i in range(1, 3):
            yield await self.get_participant_by_legacy_id(f"occ{i}")


class Bookmarks(LegacyBookmarks[MUC]):
    async def fill(self) -> None:
        pass


class TestAffiliationVersioning(UUIDFixtureMixin, SlidgeTest):
    plugin = globals()

    AFF_VER_BASE = "a58097ba3a0ae6791478dac31ae201e0ed2aa06c4aed47fe50ac3b5dc0ea41ce"
    AFF_VER_WITH_OCC3 = (
        "de90fe800d186bfe471e78643829ee5f44b5fdc9bdf55338eacff7a41fb2ed3b"
    )
    AFF_VER_WITHOUT_OCC2 = (
        "55e1628fe7e71daf123613fa594acd86006ec5857533abcfc44b44a73af8f0ef"
    )

    def setUp(self) -> None:
        super().setUp()
        self._add_slidge_user()

    def test_join_no_ver(self) -> None:
        self.__recv_join()
        self.__send_all_affiations()
        self.__send_presences()
        self.__send_subject()
        self.__send_vcard()

    def test_join_no_diff(self) -> None:
        self.__recv_join(since=self.AFF_VER_BASE)
        self.__send_presences()
        self.__send_subject()
        self.__send_vcard()

    def test_join_unknown_ver(self) -> None:
        self.__recv_join(since="unkown")
        self.__send_all_affiations()
        self.__send_presences()
        self.__send_subject()
        self.__send_vcard()

    def test_live_new_member(self) -> None:
        self.test_join_no_ver()
        muc: LegacyMUC = self.run_coro(
            self.user_session().bookmarks.by_legacy_id("room-whatever")
        )
        occ3 = self.run_coro(muc.get_participant_by_legacy_id("occ3"))
        presence = self.next_sent()
        assert isinstance(presence, Presence)
        assert presence.get_from() == occ3.jid
        self.send(  # language=XML
            f"""
            <message from='room-whatever@aim.shakespeare.lit'
                     to='romeo@shakespeare.lit/desktop'
                     type='normal'>
              <stanza-id xmlns="urn:xmpp:sid:0"
                         id="3"
                         by="room-whatever@aim.shakespeare.lit" />
              <x xmlns='http://jabber.org/protocol/muc#user'>
                <mav xmlns='urn:xmpp:muc:affiliations:1'
                     since='{self.AFF_VER_BASE}'
                     until='{self.AFF_VER_WITH_OCC3}' />
                <item jid='occ3@aim.shakespeare.lit'
                      affiliation='member' />
              </x>
              <occupant-id xmlns="urn:xmpp:occupant-id:0"
                           id="room" />
            </message>
            """,
            use_values=False,
        )

    def test_live_revoke(self) -> None:
        self.test_join_no_ver()
        muc: MUC = self.run_coro(
            self.user_session().bookmarks.by_legacy_id("room-whatever")
        )
        occ2: LegacyParticipant = self.run_coro(
            muc.get_participant_by_legacy_id("occ2")
        )
        occ2.leave()
        self.send(  # language=XML
            f"""
            <message from='room-whatever@aim.shakespeare.lit'
                     to='romeo@shakespeare.lit/desktop'
                     type='normal'>
              <stanza-id xmlns="urn:xmpp:sid:0"
                         id="3"
                         by="room-whatever@aim.shakespeare.lit" />
              <x xmlns='http://jabber.org/protocol/muc#user'>
                <mav xmlns='urn:xmpp:muc:affiliations:1'
                     since='{self.AFF_VER_BASE}'
                     until='{self.AFF_VER_WITHOUT_OCC2}' />
                <item jid='occ2@aim.shakespeare.lit'
                      affiliation='none' />
              </x>
              <occupant-id xmlns="urn:xmpp:occupant-id:0"
                           id="room" />
            </message>
            """,
            use_values=False,
        )
        presence = self.next_sent()
        assert isinstance(presence, Presence)
        assert presence.get_from() == occ2.jid

    def __recv_join(self, since: str = "") -> None:
        if since:
            mav = f"<mav xmlns='urn:xmpp:muc:affiliations:1' since='{since}' />"
        else:
            mav = "<mav xmlns='urn:xmpp:muc:affiliations:1' />"
        self.recv(  # language=XML
            f"""
            <presence to='room-whatever@aim.shakespeare.lit/romeo'
                      from='romeo@shakespeare.lit/desktop'>
              <x xmlns='http://jabber.org/protocol/muc'>{mav}</x>
            </presence>
            """
        )

    def __send_all_affiations(self) -> None:
        self.send(  # language=XML
            f"""
            <message from='room-whatever@aim.shakespeare.lit'
                     to='romeo@shakespeare.lit/desktop'>
              <x xmlns='http://jabber.org/protocol/muc#user'>
                <mav xmlns="urn:xmpp:muc:affiliations:1"
                     until='{self.AFF_VER_BASE}' />
                <item jid='occ1@aim.shakespeare.lit'
                      affiliation='member' />
                <item jid='occ2@aim.shakespeare.lit'
                      affiliation='member' />
                <item jid='romeo@shakespeare.lit'
                      affiliation='member' />
              </x>
            </message>
            """,
            use_values=False,
        )

    def __send_presences(self) -> None:
        for i in range(1, 3):
            pres = self.next_sent()
            assert isinstance(pres, Presence)
            assert pres.get_from().resource == f"occ{i}", pres
        self.send(  # language=XML
            f"""
            <presence from='room-whatever@aim.shakespeare.lit/romeo'
                      to='romeo@shakespeare.lit/desktop'>
              <x xmlns='http://jabber.org/protocol/muc#user'>
                <mav xmlns='urn:xmpp:muc:affiliations:1'
                     until='{self.AFF_VER_BASE}' />
                <item affiliation='member'
                      role='participant' />
                <status code='110' />
              </x>
              <occupant-id xmlns="urn:xmpp:occupant-id:0"
                           id="slidge-user" />
            </presence>
            """,
            use_values=False,
        )

    def __send_subject(self) -> None:
        subject = self.next_sent()
        assert isinstance(subject, Message)
        assert "subject" in subject, subject

    def __send_vcard(self) -> None:
        _vcard = self.next_sent()
        assert self.next_sent() is None
