import os
from base64 import b64encode
from binascii import hexlify
from time import time as timestamp
from typing import BinaryIO, Dict, List, Literal, Optional, Union
from uuid import UUID
from .sockets import AsyncWss
from ..lib.objects import *
from ..lib.util import deprecated, get_file_type
from ..lib.async_sessions import AsyncSession

__all__ = ('AsyncClient',)


class AsyncClient(AsyncWss, AsyncSession):
    """Represents a amino global client.

    Parameters
    ----------
    deviceId : str, optional
        The device of the client. If not provided, a random one is generated.
    proxies : dict, optional
        Proxies for HTTP requests supported by the httpx library (https://www.python-httpx.org/advanced/#routing)
    trace : bool, optional
        Show websocket trace (logs). Default is False.
    bot : bool, optional
        The client is a community bot. Default is False (command supported).

    Attributes
    ----------
    deviceId : str
        The device of the client.
    proxies : dict, None
        The proxies of the client.
    secret : str, None
        The secret password of the user account.
    sid : str, optional
        The session ID of the user account.
    uid : str, optional
        The user ID of the user account.
    trace : bool
        Show websocket trace (logs).
    session : httpx.Client
        The HTTP client object.
    app_headers : dict
        The HTTP headers of the app client.
    web_headers : dict
        The HTTP headers of the web client.

    """
    def __init__(self, deviceId: Optional[str] = None, proxies: Optional[Dict[str, str]] = None, trace: bool = False, bot: bool = False) -> None:
        self.trace = trace
        AsyncWss.__init__(self, self, trace=self.trace, is_bot=bot)
        AsyncSession.__init__(self, proxies=proxies, deviceId=deviceId)

    def change_lang(self, lang: str = "en") -> None:
        """Change the content language.

        Parameters
        ----------
        lang : str, optional
            The country language (ISO 3166-1 alfa-2), by default "en"

        """
        self.updateHeaders(lang=lang)

    async def sid_login(self, sid: str, socket = False) -> Account:
        """Login via session ID.

        Parameters
        ----------
        sid : str
            The amino session ID.
        socket : bool, optional
            Run the websocket after login. Default is False.

        Returns
        -------
        Account
            The user account.

        """
        self.settings(sid=sid)
        info = await self.get_account_info()
        self.settings(uid=info.userId)
        if socket:
            await self.launch()
        return info

    async def login(
        self,
        email: Optional[str] = None,
        password: Optional[str] = None,
        secret: Optional[str] = None,
        socket: bool = False,
    ) -> Login:
        """Login via email.

        Parameters
        ----------
        email : str, optional
            The account email. Default is None.
        password : str, optional
            The account password. Default is None.
        secret : str, optional
            The account secret password. Default is None.
        socket : bool, optional
            Run the websocket after login. Default is False.

        Returns
        -------
        Login
            The login object.

        Raises
        ------
        ValueError
            If no valid login info provided.

        """
        if not (email and (password or secret)):
            raise ValueError("Please provide VALID login info")
        data = {
            "email": email if email else "",
            "secret": f"0 {password}" if password else secret,
            "clientType": 100,
            "action": "normal",
            "deviceID": self.deviceId,
            "v": 2,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/auth/login", data)
        self.settings(sid=req["sid"], uid=req["auid"], secret=req["secret"])
        if socket or self.is_bot:
            await self.launch()
        return Login(req)

    async def logout(self) -> Json:
        """Logout from the account.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "deviceID": self.deviceId,
            "clientType": 100,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/auth/logout", data)
        self.settings(sid=None, uid=None, secret=None)
        if self.isOpened:
            await self.close()
        return Json(req)

    async def check_device(self, deviceId: str) -> Json:
        """Check if the device is avalible.

        Parameters
        ----------
        deviceId : str
            The device to check.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "deviceID": deviceId,
            "timestamp": int(timestamp() * 1000),
            "clientType": 100,
        }
        req = await self.postRequest("/g/s/device/", data)
        return Json(req)

    async def upload_media(self, file: BinaryIO, fileType: Literal['audio', 'image']) -> str:
        """Upload a media to the amino server.

        Parameters
        ----------
        file : BinaryIO
            The file opened in read-byte mode (rb).
        fileType : str
            The file type (audio, image).

        Returns
        -------
        str
            The url of the uploaded image.

        """
        if fileType not in ('image', 'audio'):
            raise ValueError("fileType must be 'audio' or 'image' not %r." % fileType)
        if fileType == "audio":
            ftype = "audio/" + get_file_type(file.name, 'acc')
        else:
            ftype = "image/" + get_file_type(file.name, 'jpg')
        newHeaders = {"content-type": ftype, "content-length": str(len(file.read()))}
        req = await self.postRequest("/g/s/media/upload", data=file, newHeaders=newHeaders)
        return req["mediaValue"]

    @deprecated(upload_media.__name__)
    async def upload_image(self, image: BinaryIO) -> str:
        """Upload an image to the amino server.

        Parameters
        ----------
        image : BinaryIO
            The image file opened in read-byte mode (rb).

        Returns
        -------
        str
            The url of the uploaded image.

        """
        return await self.upload_media(image, 'image')

    async def send_verify_code(self, email: str) -> Json:
        """Request verification code via email.

        Parameters
        ----------
        email : str
            The email to send the code.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "identity": email,
            "type": 1,
            "deviceID": self.deviceId,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/auth/request-security-validation", data)
        return Json(req)

    async def accept_host(self, requestId: str, chatId: str) -> Json:
        """Accept the chat host transfer request.

        Parameters
        ----------
        requestId : str
            The transfer request ID.
        chatId : str
            The chat ID associated with the transfer.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/transfer-organizer/{requestId}/accept")
        return Json(req)

    async def verify_account(self, email: str, code: str) -> Json:
        """Complete the account verification.

        Parameters
        ----------
        email : str
            The account email to verify.
        code : str
            The verification code.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "type": 1,
            "identity": email,
            "data": {"code": code},
            "deviceID": self.deviceId,
        }
        req = await self.postRequest("/g/s/auth/activate-email", data)
        return Json(req)

    async def restore(self, email: str, password: str) -> Json:
        """Restore an account (after deletion).

        Parameters
        ----------
        email : str
            The account email to restore.
        password : str
            The account password.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "secret": f"0 {password}",
            "deviceID": self.deviceId,
            "email": email,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/account/delete-request/cancel", data)
        return Json(req)

    async def delete_account(self, password: Optional[str] = None, secret: Optional[str] = None) -> Json:
        """Delete a user account.

        Parameters
        ----------
        password : str, optional
            The password of the account.
        secret : str, optional
            The secret password of the account.

        Returns
        -------
        Json
            The JSON response.

        Raises
        ------
        ValueError
            If the password and secret is not provided.

        """
        if not (password and secret):
            raise ValueError("Please provide a valid account info")
        data = {
            "deviceID": self.deviceId,
            "secret": f"0 {password}" if password else secret,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/account/delete-request", data)
        return Json(req)

    async def get_account_info(self) -> Account:
        """Get account information.

        Returns
        -------
        Account
            The user account object.

        """
        req = await self.getRequest("/g/s/account")
        return Account(req["account"])

    async def claim_coupon(self) -> Json:
        """Claim the new-user coupon.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest("/g/s/coupon/new-user-coupon/claim")
        return Json(req)

    async def change_amino_id(self, aminoId: str) -> Json:
        """Change the account amino ID.

        Parameters
        ----------
        aminoId : str
            The new amino ID.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {"aminoId": aminoId, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest("/g/s/account/change-amino-id", data)
        return Json(req)

    async def get_my_communities(self, start: int = 0, size: int = 25) -> CommunityList:
        """Get a list of the user's joined communities.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        CommunityList
            The joined community list object.

        """
        req = await self.getRequest(f"/g/s/community/joined?v=1&start={start}&size={size}")
        return CommunityList(req["communityList"]).CommunityList

    async def get_chat_threads(self, start: int = 0, size: int = 25) -> ThreadList:
        """Get a list of the user's joined chats.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        ThreadList
            The joined community list object.

        """
        req = await self.getRequest(f"/g/s/chat/thread?type=joined-me&start={start}&size={size}")
        return ThreadList(req["threadList"]).ThreadList

    async def get_chat_info(self, chatId: str) -> Thread:
        """Get chat information.

        Parameters
        ----------
        chatId : str
            The chat ID to get information.

        Returns
        -------
        Thread
            The chat object.

        """
        req = await self.getRequest(f"/g/s/chat/thread/{chatId}")
        return Thread(req["thread"]).Thread

    async def leave_chat(self, chatId: str) -> Json:
        """Leave a chat.

        Parameters
        ----------
        chatId : str
            The chat ID to leave.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.deleteRequest(f"/g/s/chat/thread/{chatId}/member/{self.uid}")
        return Json(req)

    async def join_chat(self, chatId: str) -> Json:
        """Join a chat.

        Parameters
        ----------
        chatId : str
            The chat ID to join.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/member/{self.uid}")
        return Json(req)

    async def start_chat(
        self,
        userId: Union[str, list],
        title: Optional[str] = None,
        message: Optional[str] = None,
        content: Optional[str] = None,
        chatType: int = 0,
    ) -> Thread:
        """Start a chat.

        Parameters
        ----------
        userId : str, list
            The user ID to chat or list of user IDs to chat.
        title : str, optional
            The chat title. Default is None.
        message : str, optional
            The initial message. Default is None.
        content : str, optional
            The chat description. Default is None.
        chatType : int, optional
            The chat type. Default is 0.
                0: DM
                1: Private
                2: Public

        Returns
        -------
        Thread
            The new chat object.

        """
        if not isinstance(userId, list):
            userId = [userId]
        data = {
            "title": title,
            "inviteeUids": userId,
            "initialMessageContent": message,
            "content": content,
            "type": chatType,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest(f"/g/s/chat/thread", data)
        return Thread(req['thread']).Thread

    async def get_from_link(self, link: str) -> FromCode:
        """Get data from a link.

        Parameters
        ----------
        link : str
            The link to get data.

        Returns
        -------
        FromCode
            The link data object.

        """
        req = await self.getRequest(f"/g/s/link-resolution?q={link}")
        return FromCode(req["linkInfoV2"]["extensions"]).FromCode

    async def edit_profile(
        self,
        nickname: Optional[str] = None,
        content: Optional[str] = None,
        icon: Optional[BinaryIO] = None,
        backgroundColor: Optional[str] = None,
        backgroundImage: Optional[str] = None,
        defaultBubbleId: Optional[str] = None,
    ) -> Json:
        """Edit the global profile.

        Parameters
        ----------
        nickname : str, optional
            The new nickname. Default is None.
        content : str, optional
            The new bio. Default is None.
        icon : BinaryIO, optional
            The opened file in read bytes mode. Default is None.
        backgroundColor : str, optional
            The new background color in hex code. Default is None.
        backgroundImage : str, optional
            The new background image. Default is None.
        defaultBubbleId : str, optional
            The new default bubble ID. Default is None.

        Returns
        -------
        Json
            The JSON response.

        """
        extensions, data = {}, {
            "address": None,
            "latitude": 0,
            "longitude": 0,
            "mediaList": None,
            "eventSource": "UserProfileView",
            "timestamp": int(timestamp() * 1000),
        }
        if content:
            data["content"] = content
        if nickname:
            data["nickname"] = nickname
        if icon:
            data["icon"] = self.upload_media(icon, 'image')
        if defaultBubbleId:
            extensions["defaultBubbleId"] = defaultBubbleId
        if backgroundColor:
            extensions["style"] = {"backgroundColor": backgroundColor}
        if backgroundImage:
            extensions["style"] = {"backgroundMediaList": [[100, backgroundImage, None, None, None]]}
        if extensions:
            data["extensions"] = extensions
        req = await self.postRequest(f"/g/s/user-profile/{self.uid}", data)
        return Json(req)

    async def flag_community(self, comId: str, reason: str, flagType: int = 0) -> Json:
        """Flag a community.

        Parameters
        ----------
        comId : str
            The community ID to flag.
        reason : str
            The reason for the flag.
        flagType : int, optional
            The type of the flag. Default is 0.
                0: bully
                2: spam
                4: off-topic
                106: violence
                107: hate
                108: suicide
                109: troll
                110: nudity

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "objectId": comId,
            "objectType": 16,
            "flagType": flagType,
            "message": reason,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest(f"/x{comId}/s/g-flag", data)
        return Json(req)

    async def leave_community(self, comId: int) -> Json:
        """Leave a community.

        Parameters
        ----------
        comId : str
            The community ID to leave.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"x/{comId}/s/community/leave")
        return Json(req)

    async def join_community(self, comId: int, invId: Optional[str] = None) -> Json:
        """Join a community.

        Parameters
        ----------
        comId : int
            The community ID to join.
        invId : str, optional
            The community invitation ID. Default is None.

        Returns
        -------
        Json
            The JSON response.

        """
        data: Dict[str, Union[str, int]] = {"timestamp": int(timestamp() * 1000)}
        if invId:
            data["invitationId"] = invId
        req = await self.postRequest(f"/x{comId}/s/community/join", data)
        return Json(req)

    async def flag(
        self,
        reason: str,
        flagType: int = 0,
        blogId: Optional[str] = None,
        wikiId: Optional[str] = None,
        userId: Optional[str] = None
    ) -> Json:
        """Flag a post or user.

        Parameters
        ----------
        reason : str
            The reason of the flag.
        flagType : int, optional
            The type of the flag. Default is 0.
                0: bully
                2: spam
                4: off-topic
                106: violence
                107: hate
                108: suicide
                109: troll
                110: nudity
        blogId : str, optional
            The blog ID to flag.
        wikiId : str, optional
            The wiki ID to flag.
        userId : str, optional
            The user ID to flag.

        Returns
        -------
        Json
            The JSON response.

        Raises
        ------
        ValueError
            If the blogId, wikiId and userId are not provided.

        """
        data = {
            "message": reason,
            "flagType": flagType,
            "timestamp": int(timestamp() * 1000)
        }
        if userId:
            data["objectId"] = userId
            data["objectType"] = 0
        elif blogId:
            data["objectId"] = blogId
            data["objectType"] = 1
        elif wikiId:
            data["objectId"] = wikiId
            data["objectType"] = 2
        else:
            raise ValueError("Please put blog, user or wiki Id")
        req = await self.postRequest("/g/s/flag", data)
        return Json(req)

    async def unfollow(self, userId: str) -> Json:
        """Unfollow a user.

        Parameters
        ----------
        userId : str
            The user ID to unfollow.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/user-profile/{userId}/member/{self.uid}")
        return Json(req)

    async def follow(self, userId: Union[str, list]) -> Json:
        """Follow a user or users.

        Parameters
        ----------
        userId : str, list
            The user ID or list of user IDs to follow.

        Returns
        -------
        Json
            The JSON response.

        """
        data: Dict[str, Union[list, int]] = {"timestamp": int(timestamp() * 1000)}
        if isinstance(userId, str):
            link = f"/g/s/user-profile/{userId}/member"
        elif isinstance(userId, list):
            data["targetUidList"] = userId
            link = f"/g/s/user-profile/{self.uid}/joined"
        req = await self.postRequest(link, data)
        return Json(req)

    async def get_member_following(self, userId: str, start: int = 0, size: int = 25) -> UserProfileList:
        """Get user's followings.

        Parameters
        ----------
        userId : str
            The user ID to get the list.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        UserProfileList
            The follwing member list object.

        """
        req = await self.getRequest(f"/g/s/user-profile/{userId}/joined?start={start}&size={size}")
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_member_followers(self, userId: str, start: int = 0, size: int = 25) -> UserProfileList:
        """Get user's followers.

        Parameters
        ----------
        userId : str
            The user ID to get the list.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        UserProfileList
            The follower list object.

        """
        req = await self.getRequest(f"/g/s/user-profile/{userId}/member?start={start}&size={size}")
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_member_visitors(self, userId: str, start: int = 0, size: int = 25) -> VisitorsList:
        """Get user's visitor list.

        Parameters
        ----------
        userId : str
            The user ID to get the list.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        VisitorsList
            The visitor list object.
        """
        req = await self.getRequest(f"/g/s/user-profile/{userId}/visitors?start={start}&size={size}")
        return VisitorsList(req["visitors"]).VisitorsList

    async def get_blocker_users(self, start: int = 0, size: int = 25) -> List[str]:
        """Get blocker user ID list.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        list
            The blocker user ID list.
        """
        req = await self.getRequest(f"/g/s/block/full-list?start={start}&size={size}")
        return req["blockerUidList"]

    async def get_blocked_users(self, start: int = 0, size: int = 25) -> List[str]:
        """Get blocked user ID list.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        list
            The blocked user ID list.
        """
        req = await self.getRequest(f"/g/s/block/full-list?start={start}&size={size}")
        return req["blockedUidList"]

    async def get_wall_comments(self, userId: str, sorting: str = "newest", start: int = 0, size: int = 25) -> CommentList:
        """Get a list of comment in a user's wall.

        Parameters
        ----------
        userId : str
            The user ID to get the list.
        sorting : str, optional
            The sorting of the list (newest, oldest, vote). Default is 'newest'.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        CommentList
            The wall comment list object.

        Raises
        ------
        ValueError
            If the sorting is invalid.

        """
        sorting = sorting.lower().replace("top", "vote")
        if sorting not in ["newest", "oldest", "vote"]:
            raise ValueError("Sorting must be one of 'newest', 'oldest', or 'vote', not %r" % sorting)
        req = await self.getRequest(f"/g/s/user-profile/{userId}/g-comment?sort={sorting}&start={start}&size={size}")
        return CommentList(req["commentList"]).CommentList

    async def get_blog_comments(
        self,
        wikiId: Optional[str] = None,
        blogId: Optional[str] = None,
        sorting: str = "newest",
        start: int = 0,
        size: int = 25
    ) -> CommentList:
        """Get post comments.

        Parameters
        ----------
        wikiId : str, optional
            The wiki ID to get the list. Default is None.
        blogId : str, optional
            The blog ID to get the list. Default is None.
        sorting : str, optional
            The sorting of the list (newest, oldest, vote). Default is 'newest'.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        CommentList
            The post comment list object.

        Raises
        ------
        ValueError
            If the sorting or post ID is invalid.

        """
        sorting = sorting.lower().replace("top", "vote")
        if sorting not in ["newest", "oldest", "vote"]:
            raise ValueError("Please insert a valid sorting")
        if blogId:
            link = f"/g/s/blog/{blogId}/comment?sort={sorting}&start={start}&size={size}"
        elif wikiId:
            link = f"/g/s/item/{wikiId}/comment?sort={sorting}&start={start}&size={size}"
        else:
            raise ValueError("Please choose a wiki or a blog")
        req = await self.getRequest(link)
        return CommentList(req["commentList"]).CommentList

    async def send_message(
        self,
        chatId: str,
        message: Optional[str] = None,
        messageType: int = 0,
        file: Optional[BinaryIO] = None,
        fileType: Optional[Literal['audio', 'image', 'gif']] = None,
        replyTo: Optional[str] = None,
        mentionUserIds: Union[List[str], str, None] = None,
        stickerId: Optional[str] = None,
        snippetLink: Optional[str] = None,
        ytVideo: Optional[str] = None,
        snippetImage: Optional[BinaryIO] = None,
        embedId: Optional[str] = None,
        embedType: Optional[int] = None,
        embedLink: Optional[str] = None,
        embedTitle: Optional[str] = None,
        embedContent: Optional[str] = None,
        embedImage: Union[BinaryIO, str, None] = None,
    ) -> Json:
        """Send a chat message.

        Parameters
        ----------
        chatId : str
            The chat ID to send the message.
        message : str, optional
            The message to send. Default is None.
        messageType : int, optional
            The message type. Default is 0.
        file : BinaryIO, optional
            The file to send, opened in read-bytes. Default is None
        fileType : str, optional
            The file type to send (audio, image, gif). Default is None
        replyTo : str, optional
            The message ID to reply. Default is None.
        mentionUserIds : list, str, optional
            The mention user IDs. Default is None
        stickerId : str, optional
            The sticker ID to send. Default is None.
        snippetLink : str, optional
            The snippet link to send. Default is None.
        ytVideo : str, optional
            The Youtube video URL to send. Default is None.
        snippetImage : BinaryIO, optional
            The snippet image opened in read-bytes. Default is None.
        embedId : str, optional
            The embed object ID. Default is None.
        embedType : int, optional
            The embed object type. Default is None.
        embedLink : str, optional
            The embed URL. Default is None.
        embedTitle : str, optional
            The embed title. Default is None.
        embedContent : str, optional
            The embed message. Default is None.
        embedImage : BinaryIO, str, optional
            The embed image opened in read-bytes. Default is None.

        Returns
        -------
        Json
            The JSON response.

        Raises
        ------
        ValueError
            If the file type to send is invalid.

        """
        mentions, embedMedia = [], []
        if message is not None and file is None:
            message = message.replace("[@", "‎‏").replace("@]", "‬‭")
        if mentionUserIds:
            if isinstance(mentionUserIds, list):
                mentions.extend({"uid": uid} for uid in mentionUserIds)
            else:
                mentions.append({"uid": mentionUserIds})
        if embedImage:
            if not isinstance(embedImage, str):
                embedMedia = [[100, self.upload_media(embedImage, 'image'), None]]
            else:
                embedMedia = [[100, embedImage, None]]
        data = {
            "type": messageType,
            "content": message,
            "attachedObject": {
                "objectId": embedId,
                "objectType": embedType,
                "link": embedLink,
                "title": embedTitle,
                "content": embedContent,
                "mediaList": embedMedia,
            },
            "extensions": {"mentionedArray": mentions},
            "clientRefId": int(timestamp() / 10 % 100000000),
            "timestamp": int(timestamp() * 1000),
        }
        if replyTo:
            data["replyMessageId"] = replyTo
        if stickerId:
            data["content"] = None
            data["stickerId"] = stickerId
            data["type"] = 3
        if snippetLink and snippetImage:
            data["attachedObject"] = None
            data["extensions"]["linkSnippetList"] = [{
                "link": snippetLink,
                "mediaType": 100,
                "mediaUploadValue": b64encode(snippetImage.read()).decode(),
                "mediaUploadValueContentType": 'image/%s' % get_file_type(snippetImage.name, 'png')
            }]
        if ytVideo:
            data["content"] = None
            data["mediaType"] = 103
            data["mediaValue"] = ytVideo
        if file:
            data["content"] = None
            if fileType == "audio":
                data["type"] = 2
                data["mediaType"] = 110
            elif fileType == "image":
                data["mediaType"] = 100
                data["mediaUploadValueContentType"] = 'image/%s' % get_file_type(file.name, 'jpg')
                data["mediaUhqEnabled"] = False
            elif fileType == "gif":
                data["mediaType"] = 100
                data["mediaUploadValueContentType"] = "image/gif"
                data["mediaUhqEnabled"] = False
            else:
                raise ValueError(fileType)
            data["mediaUploadValue"] = b64encode(file.read()).decode()
            data["attachedObject"] = None
            data["extensions"] = None
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/message", data)
        return Json(req)

    async def get_community_info(self, comId: str) -> Community:
        """Get community information.

        Parameters
        ----------
        comId : str
            The community ID to get information.

        Returns
        -------
        Community
            The community object.

        """
        link = (f"/g/s-x{comId}/community/info"
                f"?withInfluencerList=1"
                f"&withTopicList=true"
                f"&influencerListOrderStrategy=fansCount")
        req = await self.getRequest(link)
        return Community(req["community"]).Community

    async def mark_as_read(self, chatId: str) -> Json:
        """Mark as read a chat

        Parameters
        ----------
        chatId : str
            The chat ID to mark as read.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/mark-as-read")
        return Json(req)

    async def delete_message(self, chatId: str, messageId: str) -> Json:
        """Delete a message.

        Parameters
        ----------
        chatId : str
            The chat ID associated with the message.
        messageId : str
            The message ID to delete.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.deleteRequest(f"/g/s/chat/thread/{chatId}/message/{messageId}")
        return Json(req)

    async def get_chat_messages(self, chatId: str, start: int = 0, size: int = 25) -> MessageList:
        """Get messages from a chat.

        Parameters
        ----------
        chatId : str
            The chat ID to get messages.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        MessageList
            The message list object.

        """
        req = await self.getRequest(f"/g/s/chat/thread/{chatId}/message?v=2&pagingType=t&size={size}")
        return GetMessages(req["messageList"]).GetMessages

    async def get_message_info(self, messageId: str, chatId: str) -> Message:
        """The message information.

        Parameters
        ----------
        messageId : str
            The message ID to get information.
        chatId : str
            The chat ID associated with the message.

        Returns
        -------
        Message
            The message object.

        """
        req = await self.getRequest(f"/g/s/chat/thread/{chatId}/message/{messageId}")
        return Message(req["message"]).Message

    async def tip_coins(
        self,
        coins: int,
        chatId: Optional[str] = None,
        blogId: Optional[str] = None,
        transactionId: Optional[str] = None,
    ) -> Json:
        """Send props to a post.

        Parameters
        ----------
        coins : int
            The amount of coins to send.
        chatId : str, optional
            The chat ID to send coins. Default is None.
        blogId : str, optional
            The blog ID to send coins. Default is None.
        transactionId : str, optional
            The transaction ID. Default is None.

        Returns
        -------
        Json
            The JSON response.

        Raises
        ------
        ValueError
            If the chat or blog is not provided.

        """
        if transactionId is None:
            transactionId = str(UUID(hexlify(os.urandom(16)).decode("ascii")))
        data = {
            "coins": coins,
            "tippingContext": {"transactionId": transactionId},
            "timestamp": int(timestamp() * 1000),
        }
        if chatId:
            link = f"/g/s/blog/{chatId}/tipping"
        elif blogId:
            link = f"/g/s/blog/{blogId}/tipping"
        else:
            raise ValueError("please put chat or blog Id")
        req = await self.postRequest(link, data)
        return Json(req)

    async def reset_password(self, email: str, password: str, code: str, deviceId: Optional[str] = None) -> Json:
        """Reset the account password.

        Parameters
        ----------
        email : str
            The user's email.
        password : str
            The new password.
        code : str
            The verification code.
        deviceId : str, optional
            The device ID. Default is None.

        Returns
        -------
        Json
            The JSON response.

        """
        if deviceId is None:
            deviceId = self.deviceId
        data = {
            "updateSecret": f"0 {password}",
            "emailValidationContext": {
                "data": {"code": code},
                "type": 1,
                "identity": email,
                "level": 2,
                "deviceID": deviceId,
            },
            "phoneNumberValidationContext": None,
            "deviceID": deviceId,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/auth/reset-password", data)
        return Json(req)

    async def change_password(self, password: str, newPassword: str) -> Json:
        """Change the account password without verification.

        Parameters
        ----------
        password : str
            The current account password.
        newPassword : str
            The new password.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "secret": f"0 {password}",
            "updateSecret": f"0 {newPassword}",
            "validationContext": None,
            "deviceID": self.deviceId,
        }
        req = await self.postRequest("/g/s/auth/change-password", data)
        return Json(req)

    async def get_user_info(self, userId: str) -> UserProfile:
        """Get user profile information.

        Parameters
        ----------
        userId : str
            The user ID to get information.

        Returns
        -------
        UserProfile
            The user profile object.

        """
        req = await self.getRequest(f"/g/s/user-profile/{userId}")
        return UserProfile(req["userProfile"]).UserProfile

    async def comment(self, comment: str, userId: str, replyTo: Optional[str] = None) -> Json:
        """Comment on user profile.

        Parameters
        ----------
        comment : str
            The comment to send.
        userId : str
            The user ID to send the comment.
        replyTo : str, optional
            The comment ID to reply. Default is None.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "content": comment,
            "stickerId": None,
            "type": 0,
            "eventSource": "UserProfileView",
            "timestamp": int(timestamp() * 1000),
        }
        if replyTo:
            data["respondTo"] = replyTo
        req = await self.postRequest(f"/g/s/user-profile/{userId}/g-comment", data)
        return Json(req)

    async def delete_comment(self, commentId: str, userId: str) -> Json:
        """Delete a comment.

        Parameters
        ----------
        userId : str
            The user profile ID associated with the comment.
        commentId : str
            The ID of the comment.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.deleteRequest(f"/g/s/user-profile/{userId}/g-comment/{commentId}")
        return Json(req)

    async def invite_by_host(self, chatId: str, userId: Union[str, list]) -> Json:
        """Invite a user or users to a live chat.

        Parameters
        ----------
        chatId : str
            The chat ID to invite.
        userId : str, list
            The user ID or user ID list to invite.

        Returns
        -------
        Json
            The JSON response.

        """
        if not isinstance(userId, list):
            userId = [userId]
        data = {"uidList": userId, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/avchat-members", data)
        return Json(req)

    async def kick(self, chatId: str, userId: str, rejoin: bool = True) -> Json:
        """Kick a user from the chat.

        Parameters
        ----------
        chatId : str
            The chat ID associated with the user.
        userId : str
            The user ID to kick.
        rejoin : bool, optional
            Allow rejoin to the chat. Default is True.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.deleteRequest(f"/g/s/chat/thread/{chatId}/member/{userId}?allowRejoin={rejoin.real}")
        return Json(req)

    async def get_invise_users(self, master_type = "newest", start: int = 0, size: int = 25) -> UserProfileList:
        """Get a list of global user profile.

        Parameters
        ----------
        master_type : str
            The type of the user profile (newest, online). Default is 'newest'.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.getRequest(f"/g/s/user-profile?type={master_type}&start={start}&size={size}")
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def invite_to_chat(self, chatId: str, userId: Union[str, List[str]]) -> Json:
        """Invite a user or users to global chat.

        Parameters
        ----------
        chatId : str
            The chat ID to invite.
        userId : str, list
            The user ID or user ID list to invite.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {
            "uids": userId if isinstance(userId, list) else [userId],
            "timestamp": int(timestamp() * 1000)
        }
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/member/invite", data)
        return Json(req)

    @deprecated(invite_to_chat.__qualname__)
    async def invise_invite(self, chatId: str, userId: Union[str, List[str]]) -> Json:
        """Invite a user or users to global chat.

        Parameters
        ----------
        chatId : str
            The chat ID to invite.
        userId : str, list
            The user ID or user ID list to invite.

        Returns
        -------
        Json
            The JSON response.

        """
        return await self.invite_to_chat(chatId=chatId, userId=userId)

    async def block(self, userId: str) -> Json:
        """Block an user.

        Parameters
        ----------
        userId : str
            The user ID to block.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/block/{userId}")
        return Json(req)

    async def unblock(self, userId: str) -> Json:
        """Unblock a user.

        Parameters
        ----------
        userId : str
            The user ID to unblock.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.deleteRequest(f"/g/s/block/{userId}")
        return Json(req)

    async def get_public_chats(self, filterType: str = "recommended", start: int = 0, size: int = 50) -> ThreadList:
        """Get public global chats.

        Parameters
        ----------
        filterType : str, optional
            The chat type to search. Default is 'recommended'.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        ThreadList
            The public chat list object.

        """
        req = await self.getRequest(f"/g/s/chat/thread?type=public-all&filterType={filterType}&start={start}&size={size}")
        return ThreadList(req["threadList"]).ThreadList

    async def get_content_modules(self, version: int = 2) -> Json:
        req = await self.getRequest(f"/g/s/home/discover/content-modules?v={version}")
        return Json(req)

    async def get_banner_ads(self, size: int = 25, pagingType: str = "t") -> ItemList:
        """Get a list of banner ads.

        Parameters
        ----------
        size : int, optional
            The size of the list. Default is 25 (max is 100).
        pagingType : str, optional
            The pagging type. Default is 't'.

        Returns
        -------
        ItemList
            The banner ads list object.

        """
        link = (
            f"/g/s/topic/0/feed/banner-ads"
            f"?moduleId=711f818f-da0c-4aa7-bfa6-d5b58c1464d0&adUnitId=703798"
            f"&size={size}"
            f"&pagingType={pagingType}"
        )
        req = await self.getRequest(link)
        return ItemList(req["itemList"]).ItemList

    async def get_announcements(self, lang: str = "en", start: int = 0, size: int = 20) -> BlogList:
        req = await self.getRequest(f"/g/s/announcement?language={lang}&start={start}&size={size}")
        return BlogList(req["blogList"]).BlogList

    async def get_public_ndc(self, content_language: str = "en", size: int = 25) -> CommunityList:
        """Get public communities.

        Parameters
        ----------
        content_language : str, optional
            The community content language (en, de, ru, es, pt, fr, ar). Default is 'en'.

        Returns
        -------
        CommunityList
            The public community list object.

        """
        req = await self.getRequest(f"/g/s/topic/0/feed/community?language={content_language}&type=web-explore&categoryKey=recommendation&size={size}&pagingType=t")
        return CommunityList(req["communityList"]).CommunityList

    async def search_community(self, q: str, lang: str = "en", start: int = 0, size: int = 25) -> CommunityList:
        """Search communities.

        Parameters
        ----------
        q : str
            The search query.
        lang : str, optional
            The community content language (en, de, ru, es, pt, fr, ar). Default is 'en'.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        CommunityList
            The community search result object.

        """
        req = await self.getRequest(f"/g/s/community/search?q={q}&language={lang}&completeKeyword=1&start={start}&size={size}")
        return CommunityList(req["communityList"]).CommunityList

    async def invite_to_voice_chat(self, chatId: str, userId: str) -> Json:
        """Invite a user to talk in a voice chat.

        Parameters
        ----------
        chatId : str
            The chat ID of the voice chat.
        userId : str
            The user ID to invite.

        Returns
        -------
        Json
            The JSON response.

        """
        data = {"uid": userId, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}/vvchat-presenter/invite", data)
        return Json(req)

    async def get_wallet_history(self, start: int = 0, size: int = 25) -> WalletHistory:
        """Get account wallet history.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        WalletHistory
            The wallet history object.

        """
        req = await self.getRequest(f"/g/s/wallet/coin/history?start={start}&size={size}")
        return WalletHistory(req).WalletHistory

    async def get_wallet_info(self) -> WalletInfo:
        """Get account wallet.

        Returns
        -------
        WalletInfo
            The account wallet object.

        """
        req = await self.getRequest("/g/s/wallet")
        return WalletInfo(req["wallet"]).WalletInfo

    async def get_all_users(self, usersType: str = "recent", start: int = 0, size: int = 25) -> UserProfileList:
        """Get amino user list.

        Parameters
        ----------
        usersType : str, optional
            The user type (recent, banned, featured, leaders, curators). Default is'recent'.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        UserProfileList
            The amino user list object.

        """
        req = await self.getRequest(f"/g/s/user-profile?type={usersType}&start={start}&size={size}")
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_chat_members(self, chatId: str, start: int = 0, size: int = 25) -> UserProfileList:
        """Get chat member list.

        Parameters
        ----------
        chatId : str
            The chat ID to get the list of members.
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).

        Returns
        -------
        UserProfileList
            The member list object.

        """
        req = await self.getRequest(f"/g/s/chat/thread/{chatId}/member?start={start}&size={size}&type=default&cv=1.2")
        return UserProfileList(req["memberList"]).UserProfileList

    async def get_from_id(self, objectId: str, objectType: int, comId: Optional[str] = None) -> FromCode:
        """Get info from object ID.

        Parameters
        ----------
        objectId : str
            The ID of the object.
        objectType : int
            The object type.
                0: user
                1: blog
                2: wiki
                3: comment
                7: chat message
                12: chat
                16: community
                113: sticker
        comId : str, optional
            The object community ID.

        Returns
        -------
        FromCode
            The amino object info.

        """
        data = {
            "objectId": objectId,
            "targetCode": 1,
            "objectType": objectType,
            "timestamp": int(timestamp() * 1000),
        }
        link = f"/g/s/link-resolution"
        if comId:
            link = f"/g/s-x{comId}/link-resolution"
        req = await self.postRequest(link, data)
        return FromCode(req["linkInfoV2"]["extensions"]["linkInfo"]).FromCode

    async def chat_settings(
        self,
        chatId: str,
        doNotDisturb: Optional[bool] = None,
        viewOnly: Optional[bool] = None,
        canInvite: Optional[bool] = None,
        canTip: Optional[bool] = None,
        pin: Optional[bool] = None,
        coHosts: Union[str, List[str], None] = None
    ) -> List[Json]:
        """Edit a chat setting.

        Parameters
        ----------
        chatId : str
            The chat ID to configure.
        doNotDisturb : bool, optional
            If the value is boolean, sets the option. Default is None.
        viewOnly : bool, optional
            If the value is boolean, sets the option. Default is None.
        canInvite : bool, optional
            If the value is boolean, sets the option. Default is None.
        canTip : bool, optional
            If the value is boolean, sets the option. Default is None.
        pin : bool, optional
            If the value is boolean, sets the option. Default is None.
        coHosts : str, list, optional
            The new user ID or user ID list. Default is None.

        Returns
        -------
        list
            The response of the modified settings list.

        """
        res = []
        if isinstance(doNotDisturb, bool):
            data = {"alertOption": doNotDisturb.real + 1, "timestamp": int(timestamp() * 1000)}
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/member/{self.uid}/alert", data)
            res.append(Json(req))
        if isinstance(viewOnly, bool):
            view = "enable" if viewOnly else "disable"
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/view-only/{view}")
            res.append(Json(req))
        if isinstance(canInvite, bool):
            can = "enable" if canInvite else "disable"
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/members-can-invite/{can}")
            res.append(Json(req))
        if isinstance(canTip, bool):
            can = "enable" if canTip else "disable"
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/tipping-perm-status/{canTip}")
            res.append(Json(req))
        if isinstance(pin, bool):
            action = "pin" if pin else "unpin"
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/{action}")
            res.append(Json(req))
        if coHosts:
            data = {
                "uidList": coHosts if isinstance(coHosts, list) else [coHosts],
                "timestamp": int(timestamp() * 1000)
            }
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/co-host", data)
            res.append(Json(req))
        return res

    async def like_comment(self, commentId: str, blogId: Optional[str] = None, userId: Optional[str] = None) -> Json:
        """Like a comment (blog or user profile).

        Parameters
        ----------
        commentId : str
            The comment ID to like.
        blogId : str, optional
            The blog ID associated with the comment.
        userId : str, optional
            The user profile associated with the comment.

        Returns
        -------
        Json
            The JSON response.

        Raises
        ------
        ValueError
            If the blogId and userId are not provided.

        """
        data = {"value": 4, "timestamp": int(timestamp() * 1000)}
        if userId:
            link = f"/g/s/user-profile/{userId}/comment/{commentId}/g-vote?cv=1.2&value=1"
        elif blogId:
            link = f"/g/s/blog/{blogId}/comment/{commentId}/g-vote?cv=1.2&value=1"
        else:
            raise ValueError("Please put blog or user Id")
        req = await self.postRequest(link, data)
        return Json(req)

    async def unlike_comment(self, commentId: str, blogId: Optional[str] = None, userId: Optional[str] = None) -> Json:
        """Unlike a comment (blog or user profile).

        Parameters
        ----------
        commentId : str
            The comment ID to like.
        userId : str, optional
            The user profile associated with the comment.
        blogId : str, optional
            The blog ID associated with the comment.

        Returns
        -------
        Json
            The JSON response.

        Raises
        ------
        ValueError
            If the userId and blogId are not provided.

        """
        if userId:
            link = f"/g/s/user-profile/{userId}/comment/{commentId}/g-vote?eventSource=UserProfileView"
        elif blogId:
            link = f"/g/s/blog/{blogId}/comment/{commentId}/g-vote?eventSource=PostDetailView"
        else:
            raise ValueError("Please put blog or user Id")
        req = await self.deleteRequest(link)
        return Json(req)

    async def register(self, nickname: str, email: str, password: str, code: str, deviceId: Optional[str] = None) -> Json:
        """Register a new account.

        Parameters
        ----------
        nickname : str
            The nickname of the account.
        email : str
            The account email address.
        password : str
            The account password.
        code : str
            The verification code.
        deviceId : str, optional
            The account device. If not provided the current device is used.

        Returns
        -------
        Json
            The JSON response.

        """
        if not deviceId:
            deviceId = self.deviceId
        data = {
            "secret": f"0 {password}",
            "deviceID": deviceId,
            "email": email,
            "clientType": 100,
            "nickname": nickname,
            "latitude": 0,
            "longitude": 0,
            "address": None,
            "clientCallbackURL": "narviiapp://relogin",
            "validationContext": {"data": {"code": code}, "type": 1, "identity": email},
            "type": 1,
            "identity": email,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest("/g/s/auth/register", data)
        return Json(req)

    async def edit_chat(
        self,
        chatId: str,
        title: Optional[str] = None,
        content: Optional[str] = None,
        icon: Optional[str] = None,
        background: Optional[str] = None,
        keywords: Optional[List[str]] = None,
        announcement: Optional[str] = None,
        pinAnnouncement: Optional[bool] = None,
    ) -> List[Json]:
        """Edit a chat.

        Parameters
        ----------
        chatId : str
            The chat ID to edit.
        title : str, optional
            The new title. If not provided, is not edited.
        content : str, optional
            The new chat description. If not provided, is not edited.
        icon : str, optional
            The new chat icon. If not provided, is not edited.
        background : str, optional
            The new chat background. If not provided, is not edited.
        keywords : list, optional
            The new chat keywords. If not provided, is not edited.
        announcement : str, optional
            The new chat announcement. If not provided, is not edited.
        pinAnnouncement : bool, optional
            The new chat pin announcement. If not provided, is not edited.

        Returns
        -------
        list
            The JSON response list.

        """
        data: Dict[str, Union[int, str, list, dict]] = {"timestamp": int(timestamp() * 1000)}
        extensions = {}
        res = []
        if title:
            data["title"] = title
        if content:
            data["content"] = content
        if icon:
            data["icon"] = icon
        if keywords:
            data["keywords"] = keywords
        if background:
            data = {
                "media": [100, background, None],
                "timestamp": int(timestamp() * 1000),
            }
            req = await self.postRequest(f"/g/s/chat/thread/{chatId}/member/{self.uid}/background", data)
            res.append(Json(req))
        if announcement:
            extensions["announcement"] = announcement
        if pinAnnouncement:
            extensions["pinAnnouncement"] = pinAnnouncement
        if extensions:
            data['extensions'] = extensions
        req = await self.postRequest(f"/g/s/chat/thread/{chatId}", data)
        res.append(Json(req))
        return res

    async def remove_cohost(self, chatId: str, userId: str) -> Json:
        """Remove a chat co-host.

        Parameters
        ----------
        chatId : str
            The chat ID associated with the user.
        userId : str
            The cohost user ID.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.deleteRequest(f"/g/s/chat/thread/{chatId}/co-host/{userId}")
        return Json(req)

    async def edit_comment(self, commentId: str, comment: str, userId: str, replyTo: Optional[str] = None) -> Comment:
        """Edit a user profile comment.

        Parameters
        ----------
        commentId : str
            The comment ID to edit.
        comment : str
            The new comment content.
        userId : str
            The user ID associated with the comment.
        replyTo : str, optional
            The comment ID to reply. Default is None.

        Returns
        -------
        Comment
            The edited comment object.

        """
        data = {"content": comment, "timestamp": int(timestamp() * 1000)}
        if replyTo:
            data["respondTo"] = replyTo
        req = await self.postRequest(f"/g/s/user-profile/{userId}/comment/{commentId}", data)
        return Comment(req).Comments

    async def get_comment_info(self, commentId: str, userId: str) -> Comment:
        """Get user profile comment information.

        Parameters
        ----------
        commentId : str
            The comment ID.
        userId : str
            The user profile ID associated with the comment.

        Returns
        -------
        Comment
            The comment object.

        """
        req = await self.getRequest(f"/g/s/user-profile/{userId}/comment/{commentId}")
        return Comment(req).Comments

    async def get_notifications(self, start: int = 0, size: int = 25, pagingType: str = "t") -> NotificationList:
        """Get global notifications.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).
        pagingType : str, optional
            The paging type to return. Default is 't'.

        Returns
        -------
        NotificationList
            _description_
        """
        req = await self.getRequest(f"/g/s/notification?pagingType={pagingType}&start={start}&size={size}")
        return NotificationList(req).NotificationList

    async def get_notices(self, start: int = 0, size: int = 25, noticeType: str = "usersV2", status: int = 1) -> NoticeList:
        """Get user global notices.

        Parameters
        ----------
        start : int, optional
            The start index. Default is 0.
        size : int, optional
            The size of the list. Default is 25 (max is 100).
        noticeType : str, optional
            The notice type to return. Default is 'usersV2'.
        status : int, optional
            The notice status to return. Default is 1.

        Returns
        -------
        NoticeList
            The notice list object.

        """
        req = await self.getRequest(f"/g/s/notice?type={noticeType}&status={status}&start={start}&size={size}")
        return NoticeList(req).NoticeList

    async def accept_promotion(self, requestId: str) -> Json:
        """Accept a promotion request.

        Parameters
        ----------
        requestId : str
            The request ID to accept.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/notice/{requestId}/accept")
        return Json(req)

    async def decline_promotion(self, requestId: str) -> Json:
        """Decline a promotion request.

        Parameters
        ----------
        requestId : str
            The request ID to decline.

        Returns
        -------
        Json
            The JSON response.

        """
        req = await self.postRequest(f"/g/s/notice/{requestId}/decline")
        return Json(req)
