import io
from asyncio import Lock

import nio
from slidge import BaseSession
from slidge.db import GatewayUser
from slidge.util.types import PseudoPresenceShow
from slixmpp.exceptions import XMPPError
from slixmpp.types import ResourceDict

from .contact import Contact, Roster
from .group import MUC, Bookmarks, Participant
from .matrix import Client

Sender = Contact | Participant
Recipient = MUC | Contact


class Session(BaseSession[Contact]):
    bookmarks: Bookmarks
    contacts: Roster

    MESSAGE_IDS_ARE_THREAD_IDS = True

    def __init__(self, user: GatewayUser) -> None:
        super().__init__(user)
        self.events_to_ignore = set[str]()
        self.send_lock = Lock()
        f = self.user.legacy_module_data
        self.matrix = Client(f["homeserver"], f["user_id"], self)  # type:ignore[arg-type]

    async def login(self) -> str:
        await self.matrix.login_token()
        await self.matrix.listen()
        self.contacts.user_legacy_id = self.matrix.user_id
        return f"Logged in as {self.matrix.user}"

    async def logout(self) -> None:
        self.matrix.stop_listen()

    async def on_presence(
        self,
        resource: str,
        show: PseudoPresenceShow,
        status: str,
        resources: dict[str, ResourceDict],
        merged_resource: ResourceDict | None,
    ) -> None:
        if not merged_resource:
            resp = await self.matrix.set_presence("offline")
        else:
            resp = await self.matrix.set_presence(
                PRESENCE_DICT[merged_resource["show"]],
                merged_resource["status"] or None,
            )
        self.log.debug("Set presence response: %s", resp)

    async def on_avatar(
        self,
        bytes_: bytes | None,
        hash_: str | None,
        type_: str | None,
        width: int | None,
        height: int | None,
    ) -> None:
        if bytes_:
            resp, _ = await self.matrix.upload(
                io.BytesIO(bytes_), type_, filesize=len(bytes_)
            )
            self.log.debug("Upload response: %s %r", type(resp), resp)
            if not isinstance(resp, nio.UploadResponse):
                raise XMPPError("internal-server-error", str(resp))
            uri = resp.content_uri
        else:
            uri = ""
        await self.matrix.set_avatar(uri)


PRESENCE_DICT: dict[PseudoPresenceShow, str] = {
    "": "online",
    "chat": "online",
    "away": "unavailable",
    "xa": "unavailable",
    "dnd": "unavailable",
}
