from .member import PartialMember, PostAuthor


class BaseParent:
    """Represents a Weverse Base Parent class.

    Inherited by:

    - :class:`ParentComment`
    - :class:`ParentPost`
    - :class:`ParentMediaPost`
    - :class:`RootPost`
    - :class:`RootMediaPost`

    Attributes
    ----------
    data: :class:`str`
        The raw data directly taken from the response generated by Weverse's API.
    body: :class:`str`
        The body of the base parent object.
    author: :class:`.member.PartialMember`
        The :class:`.member.PartialMember` object of the author who wrote the
        base parent object.
    """

    __slots__ = ("data", "body", "author")

    def __init__(self, data: dict):
        self.data: dict = data
        self.body: str = data["body"]
        self.author: PartialMember = PartialMember(data["author"])


class ParentComment(BaseParent):
    """Represents a Weverse Parent Comment. Inherits from :class:`BaseParent`.

    Shares the same attributes with :class:`BaseParent`.

    .. container:: operations

        .. describe:: x == y

            Checks if two parent comments are equal.

        .. describe:: x != y

            Checks if two parent comments are not equal.

        .. describe:: hash(x)

            Returns the parent comment's hash.

        .. describe:: str(x)

            Returns the parent comment's body.

    Attributes
    ----------
    id: :class:`str`
        The ID of the parent comment. If more information about the parent
        comment is required by the user, the :meth:`.WeverseClient.fetch_comment()`
        method can be used to fetch the actual :class:`Comment` object.
    created_at: :class:`int`
        The time the parent comment got created at, in epoch.
    """

    __slots__ = ("id", "created_at")

    def __init__(self, data: dict):
        super().__init__(data)
        self.id: str = data["commentId"]
        self.created_at: int = data["createdAt"]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Parent Comment comment_id={self.id}, body={self.body}"

    def __str__(self):
        return self.body

    def __hash__(self):
        return hash(self.id)


class ParentPost(BaseParent):
    """Represents a Weverse Parent Post. Inherits from :class:`BaseParent`.

    Shares the same attributes with :class:`BaseParent`.

    Inherited by:

    - :class:`ParentMediaPost`

    .. container:: operations

        .. describe:: x == y

            Checks if two parent posts are equal.

        .. describe:: x != y

            Checks if two parent posts are not equal.

        .. describe:: hash(x)

            Returns the parent post's hash.

        .. describe:: str(x)

            Returns the parent post's plain body.

    Attributes
    ----------
    id: :class:`str`
        The ID of the parent post. If more information about the parent
        post is required by the user, the :meth:`.WeverseClient.fetch_post()`
        method can be used to fetch the actual :class:`.post.Post` object.
    plain_body: :class:`str`
        The plain body of the parent post that does not have markdowns
        and unnecessary information.
    post_type: :class:`str`
        The post type of the parent post.
    """

    __slots__ = ("id", "plain_body", "post_type")

    def __init__(self, data: dict):
        super().__init__(data)
        self.id: str = data["postId"]
        self.plain_body: str = data["plainBody"]
        self.post_type: str = data["postType"]

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Parent Post post_id={self.id}, body={self.plain_body}"

    def __str__(self):
        return self.plain_body

    def __hash__(self):
        return hash(self.id)


class ParentMediaPost(ParentPost):
    """Represents a Weverse Parent Media Post. Inherits from
    :class:`BaseParent` and :class:`ParentPost`.

    Shares the same attributes with :class:`BaseParent` and :class:`ParentPost`.

    .. container:: operations

        .. describe:: str(x)

            Returns the parent media post's title.

    Attributes
    ----------
    title: :class:`str`
        The title of the parent media post.
    """

    __slots__ = ("title",)

    def __init__(self, data: dict):
        super().__init__(data)
        self.title: str = data["title"]

    def __repr__(self):
        return f"Parent Media Post post_id={self.id}, title={self.title}"

    def __str__(self):
        return self.title


class RootPost(ParentPost):
    """Represents a Weverse Root Post. Inherits from
    :class:`BaseParent` and :class:`ParentPost`.

    Shares the same attributes with :class:`BaseParent` and :class:`ParentPost`.

    Attributes
    ----------
    url: :class:`str`
        The URL that leads to the root post.
    section_type: :class:`str`
        The section the root post falls under.
    author: :class:`.member.PostAuthor`
        The :class:`.member.PostAuthor` object of the author
        who wrote the root post.
    """

    __slots__ = ("url", "section_type")

    def __init__(self, data: dict):
        super().__init__(data)
        self.url: str = data["shareUrl"]
        self.section_type: str = data["sectionType"]
        self.author: PostAuthor = PostAuthor(data["author"])

    def __repr__(self):
        return f"Root Post post_id={self.id}, body={self.plain_body}"


class RootMediaPost(ParentMediaPost):
    """Represents a Weverse Root Media Post. Inherits from
    :class:`BaseParent`, :class:`ParentPost` and :class:`ParentMediaPost`.

    Shares the same attributes with :class:`BaseParent`, :class:`ParentPost`
    and :class:`ParentMediaPost`.

    Attributes
    ----------
    url: :class:`str`
        The URL that leads to the root media post.
    section_type: :class:`str`
        The section the root media post falls under.
    author: :class:`.member.PostAuthor`
        The :class:`.member.PostAuthor` object of the author
        who wrote the root media post.
    """

    __slots__ = ("url", "section_type")

    def __init__(self, data: dict):
        super().__init__(data)
        self.url: str = data["shareUrl"]
        self.section_type: str = data["sectionType"]
        self.author: PostAuthor = PostAuthor(data["author"])

    def __repr__(self):
        return f"Root Media Post post_id={self.id}, title={self.title}"


class Comment:
    """Represents a Weverse Comment.

    .. container:: operations

        .. describe:: x == y

            Checks if two comments are equal.

        .. describe:: x != y

            Checks if two comments are not equal.

        .. describe:: hash(x)

            Returns the comment's hash.

        .. describe:: str(x)

            Returns the comment's body.

    Attributes
    ----------
    data: :class:`str`
        The raw data directly taken from the response generated by Weverse's API.
    id: :class:`str`
        The ID of the comment.
    body: :class:`str`
        The body of the comment.
    comment_count: :class:`int`
        The number of comments for the comment.
    like_count: :class:`int`
        The number of likes for the comment.
    created_at: :class:`int`
        The time the comment got created at, in epoch.
    author: :class:`.member.PostAuthor`
        The :class:`.member.PostAuthor` object of the author who wrote the comment.
    """

    __slots__ = (
        "data",
        "id",
        "body",
        "comment_count",
        "like_count",
        "created_at",
        "author",
    )

    def __init__(self, data: dict):
        self.data: dict = data
        self.id: str = data["commentId"]
        self.body: str = data["body"]
        self.comment_count: int = data["commentCount"]
        self.like_count: int = data["emotionCount"]
        self.created_at: int = data["createdAt"]
        self.author: PostAuthor = PostAuthor(data["author"])

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.id == other.id

        raise NotImplementedError

    def __repr__(self):
        return f"Comment comment_id={self.id}, body={self.body}"

    def __str__(self):
        return self.body

    def __hash__(self):
        return hash(self.id)

    @property
    def parent(self) -> ParentComment | ParentMediaPost | ParentPost:
        """:class:`ParentComment` | :class:`ParentPost` | :class:`ParentMediaPost`:

        - Returns the :class:`ParentComment` object if the parent is a comment.
        - Returns the :class:`ParentMediaPost` object if the parent is a media.
        - Returns the :class:`ParentPost` object if the parent is a post.
        """
        post_types = ["NORMAL", "MOMENT", "OFFICIAL"]

        if self.data["parent"]["type"] == "COMMENT":
            return ParentComment(self.data["parent"]["data"])

        if self.data["parent"]["type"] == "POST":
            if self.data["parent"]["data"]["postType"] in post_types:
                return ParentPost(self.data["parent"]["data"])

            else:
                return ParentMediaPost(self.data["parent"]["data"])

    @property
    def root(self) -> RootMediaPost | RootPost:
        """:class:`RootMediaPost` | :class:`RootPost`:

        - Returns the :class:`RootMediaPost` object if the root post is a media post.
        - Returns the :class:`RootPost` object if the root post is a normal post.
        """
        post_types = ["NORMAL", "MOMENT", "OFFICIAL"]

        if self.data["root"]["data"]["postType"] in post_types:
            return RootPost(self.data["root"]["data"])

        return RootMediaPost(self.data["root"]["data"])

    @property
    def url(self) -> str:
        """:class:`str`: Returns the URL to the comment."""
        return self.root.url + f"/comment/{self.id}"
