import os
from base64 import b64encode
from binascii import hexlify
from time import time as timestamp
from time import timezone as timezones
from typing import Union, BinaryIO
from uuid import UUID

from ..lib.objects import *
from ..lib.async_sessions import AsyncSession as Session
from .acm import AsyncAcm as Acm


class AsyncSubClient(Acm, Session):
    def __init__(self, comId: str, proxies: dict = None, acm: bool = False):
        self.proxies = proxies
        self.comId = comId

        if acm:
            Acm.__init__(self, comId=self.comId, proxies=self.proxies)
        else:
            Session.__init__(self, proxies=self.proxies)

    async def get_video_rep_info(self, chatId: str):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/avchat-reputation"
        )
        return RepInfo(req)

    async def claim_video_rep(self, chatId: str):
        req = await self.postRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/avchat-reputation"
        )
        return RepInfo(req)

    async def join_chat(self, chatId: str = None):
        req = await self.postRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}"
        )
        return Json(req)

    async def upload_media(self, file: BinaryIO, fileType: str):
        if fileType == "audio":
            fileType = "audio/aac"
        elif fileType == "image":
            fileType = "image/jpg"
        else:
            raise TypeError(fileType)

        data = file.read()
        newHeaders = {"content-type": fileType, "content-length": str(len(data))}

        req = await self.postRequest("/g/s/media/upload", data=data, newHeaders=newHeaders)
        return req["mediaValue"]

    async def leave_chat(self, chatId: str = None):
        req = await self.deleteRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}"
        )
        return Json(req)

    async def get_member_following(self, userId: str = None, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/user-profile/{userId}/joined?start={start}&size={size}"
        )
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_member_followers(self, userId: str = None, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/user-profile/{userId}/member?start={start}&size={size}"
        )
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_chat_threads(self, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/thread?type=joined-me&start={start}&size={size}"
        )
        return ThreadList(req["threadList"]).ThreadList

    async def get_member_visitors(self, userId: str, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/user-profile/{userId}/visitors?start={start}&size={size}"
        )
        return VisitorsList(req["visitors"]).VisitorsList

    async def get_chat_messages(self, chatId: str, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/message?v=2&pagingType=t&size={size}"
        )
        return MessageList(req["messageList"]).MessageList

    async def get_user_info(self, userId: str):
        req = await self.getRequest(f"/x{self.comId}/s/user-profile/{userId}")
        return UserProfile(req["userProfile"]).UserProfile

    async def get_user_blogs(self, userId: str, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/blog?type=user&q={userId}&start={start}&size={size}"
        )
        return BlogList(req["blogList"]).BlogList

    async def get_user_wikis(self, userId: str, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/item?type=user-all&start={start}&size={size}&cv=1.2&uid={userId}"
        )
        return WikiList(req["itemList"]).WikiList

    async def get_all_users(self, usersType: str = "recent", start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/user-profile?type={usersType}&start={start}&size={size}"
        )
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_chat_members(self, start: int = 0, size: int = 25, chatId: str = None):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/member?start={start}&size={size}&type=default&cv=1.2"
        )
        return UserProfileList(req["memberList"]).UserProfileList

    async def get_chat_info(self, chatId: str):
        req = await self.getRequest(f"/x{self.comId}/s/chat/thread/{chatId}")
        return Thread(req["thread"]).Thread

    async def get_online_users(self, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/live-layer?topic=ndtopic:x{self.comId}:online-members&start={start}&size={size}"
        )
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def get_public_chats(
        self, filterType: str = "recommended", start: int = 0, size: int = 50
    ):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/thread?type=public-all&filterType={filterType}&start={start}&size={size}"
        )
        return ThreadList(req["threadList"]).ThreadList

    async def send_message(
        self,
        chatId: str,
        message: str = None,
        messageType: int = 0,
        file: BinaryIO = None,
        fileType: str = None,
        replyTo: str = None,
        mentionUserIds: Union[list, str] = None,
        stickerId: str = None,
        snippetLink: str = None,
        ytVideo: str = None,
        snippetImage: BinaryIO = None,
        embedId: str = None,
        embedType: int = None,
        embedLink: str = None,
        embedTitle: str = None,
        embedContent: str = None,
        embedImage: BinaryIO = None,
    ):

        if message is not None and file is None:
            message = message.replace("[@", "‎‏").replace("@]", "‬‭")

        mentions = []
        if mentionUserIds:
            if isinstance(mentionUserIds, list):
                for mention_uid in mentionUserIds:
                    mentions.append({"uid": mention_uid})
            mentions.append({"uid": mentionUserIds})

        if embedImage:
            if not isinstance(embedImage, str):
                embedImage = [[100, self.upload_image(embedImage, "image"), None]]
            embedImage = [[100, embedImage, None]]

        data = {
            "type": messageType,
            "content": message,
            "attachedObject": {
                "objectId": embedId,
                "objectType": embedType,
                "link": embedLink,
                "title": embedTitle,
                "content": embedContent,
                "mediaList": embedImage,
            },
            "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/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/jpg"
                data["mediaUhqEnabled"] = False

            elif fileType == "gif":
                data["mediaType"] = 100
                data["mediaUploadValueContentType"] = "image/gif"
                data["mediaUhqEnabled"] = False

            else:
                raise TypeError(fileType)

            data["mediaUploadValue"] = b64encode(file.read()).decode()
            data["attachedObject"] = None
            data["extensions"] = None

        req = await self.postRequest(f"/x{self.comId}/s/chat/thread/{chatId}/message", data)
        return Json(req)

    async def send_web_message(
        self,
        chatId: str,
        message: str = None,
        messageType: int = 0,
        icon: str = None,
        comId: str = None,
    ):
        if comId:
            self.comId = comId

        data = {
            "ndcId": f"x{self.comId}",
            "threadId": chatId,
            "message": {
                "content": message,
                "mediaType": 0,
                "type": messageType,
                "sendFailed": False,
                "clientRefId": 0,
            },
        }

        if icon:
            data["message"]["content"] = None
            data["message"]["uploadId"] = 0
            data["message"]["mediaType"] = 100
            data["message"]["mediaValue"] = icon

        return Json(await self.postRequest("/add-chat-message", data, webRequest=True))

    async def unfollow(self, userId: str):
        req = await self.postRequest(
            f"/x{self.comId}/s/user-profile/{userId}/member/{self.uid}"
        )
        return Json(req)

    async def follow(self, userId: Union[str, list]):
        data = {"timestamp": int(timestamp() * 1000)}

        if isinstance(userId, str):
            link = f"/x{self.comId}/s/user-profile/{userId}/member"
        elif isinstance(userId, list):
            link = f"/x{self.comId}/s/user-profile/{self.uid}/joined"
            data["targetUidList"] = userId

        req = await self.postRequest(link, data)
        return Json(req)

    async def start_chat(
        self,
        userId: Union[str, list],
        title: str = None,
        message: str = None,
        content: str = None,
        chatType: int = 0,
    ):
        if isinstance(userId, str):
            userId = [userId]

        data = {
            "title": title,
            "inviteeUids": userId,
            "initialMessageContent": message,
            "content": content,
            "type": chatType,
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/chat/thread", data)
        return Thread(req['thread']).Thread

    async def invite_to_chat(self, userId: Union[str, list], chatId: str = None):
        if isinstance(userId, str):
            userId = [userId]

        data = {"uids": userId, "timestamp": int(timestamp() * 1000)}

        req = await self.postRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/member/invite", data=data
        )
        return Json(req)

    async def edit_profile(
        self,
        nickname: str = None,
        content: str = None,
        icon: BinaryIO = None,
        backgroundColor: str = None,
        backgroundImage: str = None,
        defaultBubbleId: str = None,
    ):
        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:
            data["extensions"] = {"defaultBubbleId": defaultBubbleId}
        if backgroundColor:
            data["extensions"]["style"]["backgroundColor"] = backgroundColor
        if backgroundImage:
            data["extensions"]["style"]["backgroundMediaList"] = [
                [100, backgroundImage, None, None, None]
            ]

        req = await self.postRequest(f"/x{self.comId}/s/user-profile/{self.uid}", data)
        return Json(req)

    async def edit_chat(
        self,
        chatId: str,
        title: str = None,
        content: str = None,
        icon: str = None,
        background: str = None,
        keywords: list = None,
        announcement: str = None,
        pinAnnouncement: bool = None,
    ):
        res, data = [], {"timestamp": int(timestamp() * 1000)}

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

        req = await self.postRequest(f"/x{self.comId}/s/chat/thread/{chatId}", data)
        res.append(Json(req))

        return res

    async def chat_settings(
        self,
        chatId: str,
        viewOnly: bool = False,
        doNotDisturb: bool = True,
        canInvite: bool = False,
        canTip: bool = None,
        pin: bool = None,
        coHosts: Union[str, list] = None,
    ):
        res = []

        if doNotDisturb:
            if doNotDisturb:
                opt = 2
            else:
                opt = 1

            data = {"alertOption": opt, "timestamp": int(timestamp() * 1000)}
            req = await self.postRequest(
                f"/x{self.comId}/s/chat/thread/{chatId}/member/{self.uid}/alert", data
            )
            res.append(Json(req))

        if viewOnly:
            if viewOnly:
                viewOnly = "enable"
            else:
                viewOnly = "disable"

            req = await self.postRequest(
                f"/x{self.comId}/s/chat/thread/{chatId}/view-only/{viewOnly}"
            )
            res.append(Json(req))

        if canInvite:
            if canInvite:
                canInvite = "enable"
            else:
                canInvite = "disable"

            req = await self.postRequest(
                f"/x{self.comId}/s/chat/thread/{chatId}/members-can-invite/{canInvite}"
            )
            res.append(Json(req))

        if canTip:
            if canTip:
                canTip = "enable"
            else:
                canTip = "disable"
            req = await self.postRequest(
                f"/x{self.comId}/s/chat/thread/{chatId}/tipping-perm-status/{canTip}"
            )
            res.append(Json(req))

        if pin:
            if pin:
                pin = "pin"
            else:
                pin = "unpin"

            req = await self.postRequest(f"/x{self.comId}/s/chat/thread/{chatId}/{pin}")
            res.append(Json(req))

        if coHosts:
            data = {"uidList": coHosts, "timestamp": int(timestamp() * 1000)}
            req = await self.postRequest(f"{self.comId}/s/chat/thread/{chatId}/co-host", data)
            res.append(Json(req))

        return res

    async def like_blog(self, blogId: str = None, wikiId: str = None):
        data = {"value": 4, "timestamp": int(timestamp() * 1000)}

        if blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/vote?cv=1.2&value=4"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/vote?cv=1.2&value=4"
        else:
            raise TypeError("Please put wiki or blog Id")

        req = await self.postRequest(link, data)
        return Json(req)

    async def unlike_blog(self, blogId: str = None, wikiId: str = None):
        if blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/vote?eventSource=FeedList"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/vote?eventSource=FeedList"
        else:
            raise TypeError("Please put wikiId or blogId")

        req = await self.deleteRequest(link)
        return Json(req)

    async def change_titles(self, userId: str, titles: list, colors: list):
        t = []
        for title, color in zip(titles, colors):
            t.append({"title": title, "color": color})
        data = {
            "adminOpName": 207,
            "adminOpValue": {"titles": t},
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/user-profile/{userId}/admin", data)
        return Json(req)

    async def like_comment(
        self, commentId: str, blogId: str = None, wikiId: str = None, userId: str = None
    ):
        data = {"value": 1}

        if blogId:
            data["eventSource"] = "PostDetailView"
            link = f"/x{self.comId}/s/blog/{blogId}/comment/{commentId}/vote?cv=1.2&value=1"
        elif wikiId:
            data["eventSource"] = "PostDetailView"
            link = f"/x{self.comId}/s/item/{wikiId}/comment/{commentId}/vote?cv=1.2&value=1"
        elif userId:
            data["eventSource"] = "UserProfileView"
            link = f"/x{self.comId}/s/user-profile/{userId}/comment/{commentId}/vote?cv=1.2&value=1"
        else:
            raise TypeError("Please put a wiki or user or blog Id")

        req = await self.postRequest(link, data)
        return Json(req)

    async def unlike_comment(
        self, commentId: str, blogId: str = None, wikiId: str = None, userId: str = None
    ):
        if blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/comment/{commentId}/g-vote?eventSource=PostDetailView"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/comment/{commentId}/g-vote?eventSource=PostDetailView"
        elif userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/comment/{commentId}/g-vote?eventSource=UserProfileView"
        else:
            raise TypeError("Please put a wiki or user or blog Id")

        req = await self.deleteRequest(link)
        return Json(req)

    async def comment(
        self,
        comment: str,
        userId: str = None,
        blogId: str = None,
        wikiId: str = None,
        replyTo: str = None,
        isGuest: bool = False,
    ):
        data = {"content": comment, "timestamp": int(timestamp() * 1000)}

        if replyTo:
            data["respondTo"] = replyTo
        if isGuest:
            comType = "g-comment"
        else:
            comType = "comment"

        if userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/{comType}"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/{comType}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/{comType}"
        else:
            raise TypeError("Please put a wiki or user or blog Id")

        req = await self.postRequest(link, data)
        return Json(req)

    async def delete_comment(
        self, commentId: str, userId: str = None, blogId: str = None, wikiId: str = None
    ):
        if userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/comment/{commentId}"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/comment/{commentId}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/comment/{commentId}"
        else:
            raise TypeError("Please put blog or wiki or user Id")

        req = await self.deleteRequest(link)
        return Json(req)

    async def edit_comment(
        self,
        commentId: str,
        comment: str,
        userId: str = None,
        blogId: str = None,
        wikiId: str = None,
        replyTo: str = None,
        isGuest: bool = False,
    ):
        data = {"content": comment, "timestamp": int(timestamp() * 1000)}
        if replyTo:
            data["respondTo"] = replyTo
        if isGuest:
            comType = "g-comment"
        else:
            comType = "comment"

        if userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/{comType}/{commentId}"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/{comType}/{commentId}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/{comType}/{commentId}"
        else:
            raise TypeError("Please put blog or wiki or user Id")

        req = await self.postRequest(link, data)
        return Comment(req).Comments

    async def get_comment_info(
        self,
        commentId: str,
        userId: str = None,
        blogId: str = None,
        wikiId: str = None,
        isGuest: bool = False,
    ):
        if isGuest:
            comType = "g-comment"
        else:
            comType = "comment"

        if userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/{comType}/{commentId}"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/{comType}/{commentId}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/{comType}/{commentId}"
        else:
            raise TypeError("Please put blog or wiki or user Id")

        req = await self.getRequest(link)
        return Comment(req).Comments

    async def get_wall_comments(
        self, userId: str, sorting: str = "newest", start: int = 0, size: int = 25
    ):
        sorting = sorting.lower().replace("top", "vote")
        if sorting not in ["newest", "oldest", "vote"]:
            raise TypeError("حط تايب يا حمار")

        req = await self.getRequest(
            f"/x{self.comId}/s/user-profile/{userId}/comment?sort={sorting}&start={start}&size={size}"
        )
        return CommentList(req["commentList"]).CommentList

    async def get_blog_comments(
        self,
        wikiId: str = None,
        blogId: str = None,
        quizId: str = None,
        sorting: str = "newest",
        size: int = 25,
        start: int = 0,
    ):
        sorting = sorting.lower().replace("top", "vote")
        if sorting not in ["newest", "oldest", "vote"]:
            raise TypeError("حط تايب يا حمار")

        if quizId:
            blogId = quizId
        if blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/comment?sort={sorting}&start={start}&size={size}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/comment?sort={sorting}&start={start}&size={size}"
        else:
            raise TypeError("Please choose a wiki or a blog")

        req = await self.getRequest(link)
        return CommentList(req["commentList"]).CommentList

    async def vote_comment(self, blogId: str, commentId: str, value: bool = True):
        if value:
            value = 1
        else:
            value = -1

        data = {"value": value, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(
            f"/x{self.comId}/s/blog/{blogId}/comment/{commentId}/vote?cv=1.2&value=1",
            data,
        )
        return Json(req)

    async def vote_poll(self, blogId: str, optionId: str):
        data = {"value": 1, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(
            f"/x{self.comId}/s/blog/{blogId}/poll/option/{optionId}/vote", data
        )
        return Json(req)

    async def get_blog_info(
        self, blogId: str = None, wikiId: str = None, folderId: str = None
    ):
        if blogId:
            link = f"/x{self.comId}/s/blog/{blogId}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}"
        elif folderId:
            link = f"/x{self.comId}/s/shared-folder/files/{folderId}"
        else:
            raise TypeError("Please put a wiki or blog Id")

        req = await self.getRequest(link)
        return GetInfo(req).GetInfo

    async def get_blogs(self, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/feed/featured?start={start}&size={size}"
        )
        return BlogList(req["featuredList"]).BlogList

    async def get_blogs_more(self, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/feed/featured-more?start={start}&size={size}"
        )
        return BlogList(req["blogList"]).BlogList

    async def get_blogs_all(self, start: int = 0, size: int = 25, pagingType: str = "t"):
        req = await self.getRequest(
            f"/x{self.comId}/s/feed/blog-all?pagingType={pagingType}&start={start}&size={size}"
        )
        return RecentBlogs(req["blogList"]).RecentBlogs

    async def tip_coins(
        self,
        coins: int,
        chatId: str = None,
        blogId: str = None,
        wikiId: str = None,
        transactionId: str = None,
    ):
        if transactionId is None:
            transactionId = str(UUID(hexlify(os.urandom(16)).decode("ascii")))
        data = {
            "coins": int(coins),
            "tippingContext": {"transactionId": transactionId},
            "timestamp": int(timestamp() * 1000),
        }

        if chatId:
            link = f"/x{self.comId}/s/chat/thread/{chatId}/tipping"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/tipping"
        elif wikiId:
            link = f"/x{self.comId}/s/tipping"
            data["objectType"] = 2
            data["objectId"] = wikiId
        else:
            raise TypeError("Please put a wiki or chat or blog Id")

        req = await self.postRequest(link, data)
        return Json(req)

    async def check_in(self, timezone: int = 180):
        data = {"timezone": timezone, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(f"/x{self.comId}/s/check-in", data)
        return Json(req)

    async def check_in_lottery(self, timezone: int = 180):
        data = {"timezone": timezone, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(f"/x{self.comId}/s/check-in/lottery", data)
        return Json(req)

    async def delete_message(
        self, chatId: str, messageId: str, asStaff: bool = False, reason: str = None
    ):
        data = {"adminOpName": 102, "timestamp": int(timestamp() * 1000)}

        if asStaff and reason:
            data["adminOpNote"] = {"content": reason}
            req = await self.postRequest(
                f"/x{self.comId}/s/chat/thread/{chatId}/message/{messageId}/admin", data
            )
        else:
            req = await self.deleteRequest(
                f"/x{self.comId}/s/chat/thread/{chatId}/message/{messageId}"
            )

        return Json(req)

    async def invite_by_host(self, chatId: str, userId: Union[str, list]):
        req = await self.postRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/member/{userId}/invite-av-chat"
        )
        return Json(req)

    async def strike(self, userId: str, time: str, title: str = None, reason: str = None):
        times = {
            "1-Hours": 3600,
            "3-Hours": 10800,
            "6-Hours": 21600,
            "12-Hours": 43200,
            "24-Hours": 86400,
        }.get(time, 3600)

        data = {
            "uid": userId,
            "title": title,
            "content": reason,
            "attachedObject": {"objectId": userId, "objectType": 0},
            "penaltyType": 1,
            "penaltyValue": times,
            "adminOpNote": {},
            "noticeType": 4,
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/notice", data)
        return Json(req)

    async def ban(self, userId: str, reason: str, banType: int = None):
        data = {
            "reasonType": banType,
            "note": {"content": reason},
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/user-profile/{userId}/ban", data)
        return Json(req)

    async def unban(self, userId: str, reason: str = "هذا العضو كان شاطر اخر كم يوم"):
        data = {"note": {"content": reason}, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(f"/x{self.comId}/s/user-profile/{userId}/unban", data)
        return Json(req)

    async def hide(
        self,
        note: str = None,
        blogId: str = None,
        userId: str = None,
        wikiId: str = None,
        chatId: str = None,
    ):
        data = {
            "adminOpName": 18 if userId else 110,
            "adminOpValue": None if userId else 9,
            "timestamp": int(timestamp() * 1000),
        }

        if userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/admin"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/admin"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/admin"
        elif chatId:
            link = f"/x{self.comId}/s/chat/thread/{chatId}/admin"
        else:
            raise TypeError("Please put a wiki or user or chat or blog Id")
        if note:
            data["adminOpNote"] = {"content": note}

        req = await self.postRequest(link, data)
        return Json(req)

    async def unhide(
        self,
        note: str = None,
        blogId: str = None,
        userId: str = None,
        wikiId: str = None,
        chatId: str = None,
    ):
        data = {
            "adminOpName": 19 if userId else 110,
            "adminOpValue": 0,
            "timestamp": int(timestamp() * 1000),
        }

        if userId:
            link = f"/x{self.comId}/s/user-profile/{userId}/admin"
        elif blogId:
            link = f"/x{self.comId}/s/blog/{blogId}/admin"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}/admin"
        elif chatId:
            link = f"/x{self.comId}/s/chat/thread/{chatId}/admin"
        else:
            raise TypeError("Please put a wiki or user or chat or blog Id")
        if note:
            data["adminOpNote"] = {"content": note}

        req = await self.postRequest(link, data)
        return Json(req)

    async def send_warning(self, userId: str, reason: str = None):
        data = {
            "uid": userId,
            "title": "Custom",
            "content": reason,
            "attachedObject": {"objectId": userId, "objectType": 0},
            "penaltyType": 0,
            "adminOpNote": {},
            "noticeType": 7,
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/notice", data)
        return Json(req)

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

    async def post_blog(self, title: str, content: str, fansOnly: bool = False):
        data = {
            "extensions": {"fansOnly": fansOnly},
            "content": content,
            "latitude": 0,
            "longitude": 0,
            "title": title,
            "type": 0,
            "contentLanguage": "ar",
            "eventSource": "GlobalComposeMenu",
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/blog", data)
        return Json(req)

    async def post_wiki(
        self,
        title: str,
        content: str,
        fansOnly: bool = False,
        icon: str = None,
        backgroundColor: str = None,
        keywords: Union[str, list] = None,
    ):
        data = {
            "extensions": {
                "fansOnly": fansOnly,
                "props": [],
                "style": {"backgroundColor": backgroundColor},
            },
            "content": content,
            "keywords": keywords,
            "label": title,
            "latitude": 0,
            "longitude": 0,
            "eventSource": "UserProfileView",
            "timestamp": int(timestamp() * 1000),
        }
        if icon:
            data["icon"] = icon

        req = await self.postRequest(f"/x{self.comId}/s/item", data)
        return Json(req)

    async def delete_blog(self, blogId: str):
        req = await self.deleteRequest(f"/x{self.comId}/s/blog/{blogId}")
        return Json(req)

    async def delete_wiki(self, wikiId: str):
        req = await self.deleteRequest(f"/x{self.comId}/s/item/{wikiId}")
        return Json(req)

    async def activate_status(self, status: int = 1):
        data = {
            "onlineStatus": status,
            "duration": 86400,
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest(
            f"/x{self.comId}/s/user-profile/{self.uid}/online-status", data
        )
        return Json(req)

    async def subscribe(self, userId: str, autoRenew: str = False, transactionId: str = None):
        if transactionId is None:
            transactionId = str(UUID(hexlify(os.urandom(16)).decode("ascii")))
        data = {
            "paymentContext": {
                "transactionId": transactionId,
                "isAutoRenew": autoRenew,
            },
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/influencer/{userId}/subscribe", data)
        return Json(req)

    async def submit_wiki(self, wikiId: str, message: str = None):
        data = {
            "message": message,
            "itemId": wikiId,
            "timestamp": int(timestamp() * 1000),
        }

        req = await self.postRequest(f"/x{self.comId}/s/knowledge-base-request", data)
        return Json(req)

    async def edit_blog(
        self,
        title: str,
        content: str,
        blogId: str = None,
        wikiId: str = None,
        fansOnly: bool = False,
        backgroundColor: str = None,
        media: list = None,
    ):
        data = {
            "title": title,
            "content": content,
            "timestamp": int(timestamp() * 1000),
        }

        if media:
            data["mediaList"] = [[100, media, None, "XYZ", None, {"fileName": "Amino"}]]
        if fansOnly:
            data["extensions"]["fansOnly"] = True
        if backgroundColor:
            data["extensions"] = {"backgroundColor": backgroundColor}

        if blogId:
            link = f"/x{self.comId}/s/blog/{blogId}"
        elif wikiId:
            link = f"/x{self.comId}/s/item/{wikiId}"
        else:
            raise TypeError("Please put blogId or wikiId")

        req = await self.postRequest(link, data)
        return Json(req)

    async def get_chat_bubbles(self, start: int = 0, size: int = 20):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/chat-bubble?type=all-my-bubbles&start={start}&size={size}"
        )
        return BubbleList(req["chatBubbleList"]).BubbleList

    async def select_bubble(self, bubbleId: str, apply: int = 0, chatId: str = None):
        data = {
            "bubbleId": bubbleId,
            "applyToAll": apply,
            "timestamp": int(timestamp() * 1000),
        }
        if chatId:
            data["threadId"] = chatId

        req = await self.postRequest(f"/x{self.comId}/s/chat/thread/apply-bubble")
        return Json(req)

    async def delete_chat_bubble(self, bubbleId: str):
        req = await self.deleteRequest(url=f"/x{self.comId}/s/chat/chat-bubble/{bubbleId}")
        return Json(req)

    async def get_chat_bubble_templates(self, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/chat/chat-bubble/templates?start={start}&size={size}"
        )
        return BubbleTemplates(req["templateList"])

    async def upload_custom_bubble(self, templateId: str):
        req = await self.postRequest(
            f"/x{self.comId}/s/chat/chat-bubble/templates/{templateId}/generate"
        )
        return Json(req)

    async def kick(self, chatId: str, userId: str, rejoin: bool = True):
        if rejoin:
            rejoin = 1
        if not rejoin:
            rejoin = 0

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

    async def block(self, userId: str):
        req = await self.postRequest(f"/x{self.comId}/s/block/{userId}")
        return Json(req)

    async def flag(
        self,
        reason: str,
        flagType: str = "spam",
        userId: str = None,
        wikiId: str = None,
        blogId: str = None,
    ):
        types = {
            "violence": 106,
            "hate": 107,
            "suicide": 108,
            "troll": 109,
            "nudity": 110,
            "bully": 0,
            "off-topic": 4,
            "spam": 2,
        }

        data = {"message": reason, "timestamp": int(timestamp() * 1000), "flagType": types.get(flagType, 106)}

        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 TypeError("choose a certain type to report")

        req = await self.postRequest(f"/x{self.comId}/s/flag", data)
        return Json(req)

    async def send_active_time(self, tz: int = int(-timezones // 1000), timers: list = None):
        data = {
            "userActiveTimeChunkList": timers if timers else [{"start": int(timestamp()), "end": int(timestamp() + 300)}],
            "timestamp": int(timestamp() * 1000),
            "optInAdsFlags": 2147483647,
            "timezone": tz,
        }

        req = await self.postRequest(
            f"/x{self.comId}/s/community/stats/user-active-time", data, minify=True
        )
        return Json(req)

    async def transfer_host(self, chatId: str, userIds: list):
        data = {"uidList": userIds, "timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/transfer-organizer", data
        )
        return Json(req)

    async def accept_host(self, chatId: str, requestId: str):
        data = {"timestamp": int(timestamp() * 1000)}
        req = await self.postRequest(
            f"/x{self.comId}/s/chat/thread/{chatId}/transfer-organizer/{requestId}/accept",
            data,
        )
        return Json(req)

    async def set_cohost(self, chatId : str, asistent_id : [str, list]):
         data = {
            "uidList": asistent_id,
            "timestamp": int(timestamp() * 1000)
        }

         req = await self.postRequest(f"/x{self.comId}/s/chat/thread/{chatId}/co-host", data)
         return Json(req)


    async def del_cohost(self, chatId : str, userId : str):
           req = await self.deleteRequest(f"/x{self.comId}/s/chat/thread/{chatId}/co-host/{userId}")
           return Json(req)

    async def get_quizzes(self, quizzesType: str = "recent", start: int = 0, size: int = 25):
        link = {
            "recent": f"x{self.comId}/s/blog?type=quizzes-recent&start={start}&size={size}",
            "trending": f"x{self.comId}/s/feed/quiz-trending?start={start}&size={size}",
            "best": f"x{self.comId}/s/feed/quiz-best-quizzes?start={start}&size={size}",
        }.get(quizzesType)

        req = await self.getRequest(link)
        return BlogList(req["blogList"]).BlogList

    async def get_quiz_questions(self, quizId: str):
        req = await self.getRequest(f"/x{self.comId}/s/blog/{quizId}?action=review")
        return QuizQuestionList(req["blog"]["quizQuestionList"]).QuizQuestionList

    async def play_quiz(self, quizId: str, questions: list, answers: list, mode: int = 0):
        data = {
            "mode": mode,
            "quizAnswerList": [
                {"optIdList": [answer], "quizQuestionId": question, "timeSpent": 0.0}
                for answer, question in zip(answers, questions)
            ],
            "timestamp": int(timestamp() * 1000),
        }
        req = await self.postRequest(f"/x{self.comId}/s/blog/{quizId}/quiz/result", data)
        return req

    async def get_quiz_rankings(self, quizId: str, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/blog/{quizId}/quiz/result?start={start}&size={size}"
        )
        return QuizRankings(req).QuizRankings

    async def search_user(self, username: str, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/user-profile?type=name&q={username}&start={start}&size={size}"
        )
        return UserProfileList(req["userProfileList"]).UserProfileList

    async def search_blog(self, words: str, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/blog?type=keywords&q={words}&start={start}&size={size}"
        )
        return BlogList(req["blogList"]).BlogList

    async def get_notifications(self, size: int = 25, pagingType: str = "t"):
        req = await self.getRequest(
            f"/x{self.comId}/s/notification?pagingType={pagingType}&size={size}"
        )
        return NotificationList(req).NotificationList

    async def get_notices(
        self, start: int = 0, size: int = 25, noticeType: str = "usersV2", status: int = 1
    ):
        req = await self.getRequest(
            f"/x{self.comId}/s/notice?type={noticeType}&status={status}&start={start}&size={size}"
        )
        return NoticeList(req).NoticeList

    async def accept_promotion(self, requestId: str):
        if not isinstance(requestId, str):
            raise TypeError(f"Please use a string not {type(requestId)}")
        req = await self.postRequest(f"/x{self.comId}/s/notice/{requestId}/accept")
        return Json(req)

    async def decline_promotion(self, requestId: str):
        if not isinstance(requestId, str):
            raise TypeError(f"Please use a string not {type(requestId)}")
        req = await self.postRequest(f"/x{self.comId}/s/notice/{requestId}/decline")
        return Json(req)

    async def sendWebActive(self):
        data = {"ndcId": self.comId}
        return await self.postRequest(
            "/community/stats/web-user-active-time", data, webRequest=True
        )

    async def get_recent_blogs(self, pageToken: str = None, start: int = 0, size: int = 25):
        req = await self.getRequest(
            f"/x{self.comId}/s/feed/blog-all?pagingType=t&start={start}&size={size}"
        )
        return RecentBlogs(req["BlogList"]).RecentBlogs

    async def publish_to_featured(
        self,
        time: int,
        userId: str = None,
        chatId: str = None,
        blogId: str = None,
        wikiId: str = None,
    ):
        time = (
            {1: 3600, 2: 7200, 3: 10800}.get(time)
            if chatId
            else {1: 86400, 2: 172800, 3: 259200}.get(time)
        )

        data = {
            "adminOpName": 114,
            "adminOpValue": {"featuredDuration": time},
            "timestamp": int(timestamp() * 1000),
        }

        if userId:
            featuredType, endpoint = 4, f"user-profile/{userId}"
        elif blogId:
            featuredType, endpoint = 1, f"blog/{blogId}"
        elif wikiId:
            featuredType, endpoint = 1, f"item/{blogId}"
        elif chatId:
            featuredType, endpoint = 5, f"chat/thread/{chatId}"
        else:
            raise TypeError("Please Specify Object ID")

        data["adminOpValue"]["featuredType"] = featuredType
        req = await self.postRequest(f"/x{self.comId}/s/{endpoint}/admin", data)
        return Json(req)

    async def remove_from_featured(
        self,
        userId: str = None,
        chatId: str = None,
        blogId: str = None,
        wikiId: str = None,
    ):
        data = {
            "adminOpName": 114,
            "adminOpValue": {"featuredType": 0},
            "timestamp": int(timestamp() * 1000),
        }

        if userId:
            endpoint = f"user-profile/{userId}"
        elif blogId:
            endpoint = f"blog/{blogId}"
        elif wikiId:
            endpoint = f"item/{blogId}"
        elif chatId:
            endpoint = f"chat/thread/{chatId}"
        else:
            raise TypeError("Please Specify Object ID")

        req = await self.postRequest(f"/x{self.comId}/s/{endpoint}/admin", data)
        return Json(req)
