"""

Contains objects related to Roblox thumbnails.

"""
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .client import Client
from enum import Enum
from typing import Optional, List, Union, Tuple

from .threedthumbnails import ThreeDThumbnail
from .utilities.types import AssetOrAssetId, BadgeOrBadgeId, GamePassOrGamePassId, GroupOrGroupId, PlaceOrPlaceId, \
    UniverseOrUniverseId, UserOrUserId


class ThumbnailState(Enum):
    """
    The current state of the thumbnail.
    """

    completed = "Completed"
    in_review = "InReview"
    pending = "Pending"
    error = "Error"
    moderated = "Moderated"
    blocked = "Blocked"


class ThumbnailReturnPolicy(Enum):
    """
    The return policy for place/universe thumbnails.
    """

    place_holder = "PlaceHolder"
    auto_generated = "AutoGenerated"
    force_auto_generated = "ForceAutoGenerated"


class ThumbnailFormat(Enum):
    """
    Format returned by the endpoint.
    """

    png = "Png"
    jpeg = "Jpeg"


class AvatarThumbnailType(Enum):
    """
    Type of avatar thumbnail.
    """

    full_body = "full_body"
    headshot = "headshot"
    bust = "bust"


SizeTupleOrString = Union[Tuple[int, int], str]


def _to_size_string(size_item: SizeTupleOrString):
    if isinstance(size_item, tuple):
        return f"{int(size_item[0])}x{int(size_item[1])}"
    else:
        return size_item


class Thumbnail:
    """
    Represents a Roblox thumbnail as returned by almost all endpoints on https://thumbnails.roblox.com/.

    Attributes:
        target_id: The id of the target of the image.
        state: The current state of the image.
        image_url: Url of the image.
    """

    def __init__(self, client: Client, data: dict):
        """
        Arguments:
            client: Client object.
            data: The data from the request.
        """
        self._client: Client = client

        self.target_id: int = data["targetId"]
        self.state: ThumbnailState = ThumbnailState(data["state"])
        self.image_url: Optional[str] = data["imageUrl"]

    def __repr__(self):
        return f"<{self.__class__.__name__} target_id={self.target_id} name={self.state!r} " \
               f"image_url={self.image_url!r}>"

    async def get_3d_data(self) -> ThreeDThumbnail:
        """
        Generates 3D thumbnail data for this endpoint.
        
        Returns:
            A ThreeDThumbnail.
        """
        threed_response = await self._client.requests.get(
            url=self.image_url
        )
        threed_data = threed_response.json()
        return ThreeDThumbnail(
            client=self._client,
            data=threed_data
        )


class UniverseThumbnails:
    """
    Represents a universe's thumbnails as returned by https://thumbnails.roblox.com/v1/games/multiget/thumbnails.

    Attributes:
        universe_id: The id of the target of the image.
        error: The errors you got.
        thumbnails: List of thumbnails.
    """

    def __init__(self, client: Client, data: dict):
        """
        Arguments:
            client: Shared object.
            data: The data from the request.
        """
        self._client: Client = client
        # todo add base universe maby
        self.universe_id: int = data["universeId"]
        self.error: Optional[str] = data["error"]
        self.thumbnails: List[Thumbnail] = [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in data["thumbnails"]
        ]


class ThumbnailProvider:
    """
    The ThumbnailProvider that provides multiple functions for generating user thumbnails.
    """

    def __init__(self, client: Client):
        """
        Arguments:
            client: Client object.
        """
        self._client: Client = client

    async def get_asset_thumbnails(
            self,
            assets: List[AssetOrAssetId],
            return_policy: ThumbnailReturnPolicy = ThumbnailReturnPolicy.place_holder,
            size: SizeTupleOrString = (30, 30),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns asset thumbnails for the asset ID passed.
        Supported sizes:  
        - 30x30  
        - 42x42  
        - 50x50  
        - 60x62  
        - 75x75  
        - 110x110  
        - 140x140  
        - 150x150  
        - 160x100  
        - 160x600  
        - 250x250  
        - 256x144  
        - 300x250  
        - 304x166  
        - 384x216  
        - 396x216  
        - 420x420  
        - 480x270  
        - 512x512  
        - 576x324  
        - 700x700  
        - 728x90  
        - 768x432  

        Arguments:
            assets: Assets you want the thumbnails of.
            return_policy: How you want it returns look at enum.
            size: size of the image.
            image_format: Format of the image.
            is_circular: if the image is a circle yes or no.

        Returns:
            A list of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/assets"),
            params={
                "assetIds": list(map(int, assets)),
                "returnPolicy": return_policy.value,
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_asset_thumbnail_3d(self, asset: AssetOrAssetId) -> Thumbnail:
        """
        Returns a 3D asset thumbnail for the user ID passed.

        Arguments:
            asset: Asset you want the thumbnails of.

        Returns:
            A Thumbnail.
        """
        thumbnail_response = await self._client.requests.get(
            url=self._client.url_generator.get_url(
                "thumbnails", "v1/assets-thumbnail-3d"
            ),
            params={"assetId": int(asset)},
        )
        thumbnail_data = thumbnail_response.json()
        return Thumbnail(client=self._client, data=thumbnail_data)

    async def get_badge_icons(
            self,
            badges: List[BadgeOrBadgeId],
            size: SizeTupleOrString = (150, 150),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns badge icons for each badge ID passed.
        Supported sizes:  
        - 150x150  

        Arguments:
            badges: Badges you want the thumbnails of.
            size: size of the image.
            image_format: Format of the image.
            is_circular: if the image is a circle yes or no.

        Returns:
            A list of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/badges/icons"),
            params={
                "badgeIds": list(map(int, badges)),
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_gamepass_icons(
            self,
            gamepasses: List[GamePassOrGamePassId],
            # TODO Make size enum
            size: SizeTupleOrString = (150, 150),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns gamepass icons for each gamepass ID passed.
        Supported sizes:  
        - 150x150  

        Arguments:
            gamepasses: Gamepasses you want the thumbnails of.
            size: size of the image.
            image_format: Format of the image.
            is_circular: If the image is a circle yes or no.

        Returns:
            A list of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/game-passes"),
            params={
                "gamePassIds": list(map(int, gamepasses)),
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_universe_icons(
            self,
            universes: List[UniverseOrUniverseId],
            return_policy: ThumbnailReturnPolicy = ThumbnailReturnPolicy.place_holder,
            size: SizeTupleOrString = (50, 50),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns universe icons for each universe ID passed.
        Supported sizes:  
        - 50x50  
        - 128x128  
        - 150x150  
        - 256x256  
        - 512x512  
        - 768x432  

        Arguments:
            universes: Universes you want the thumbnails of.
            return_policy: How you want it returns look at enum.
            size: size of the image.
            image_format: Format of the image.
            is_circular: If the image is a circle yes or no.

        Returns:
            A list of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/games/icons"),
            params={
                "universeIds": list(map(int, universes)),
                "returnPolicy": return_policy.value,
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_universe_thumbnails(
            self,
            universes: List[UniverseOrUniverseId],
            size: SizeTupleOrString = (768, 432),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
            count_per_universe: int = None,
            defaults: bool = None,
    ) -> List[UniverseThumbnails]:
        """
        Returns universe thumbnails for each universe ID passed.
        Supported sizes:  
        - 768x432  
        - 576x324  
        - 480x270  
        - 384x216  
        - 256x144  

        Arguments:
            universes: Universes you want the thumbnails of.
            size: size of the image.
            image_format: Format of the image.
            count_per_universe: Unknown.
            is_circular: If the image is a circle yes or no.
            defaults: Whether to return default thumbnails.

        Returns:
            A list of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url(
                "thumbnails", "v1/games/multiget/thumbnails"
            ),
            params={
                "universeIds": list(map(int, universes)),
                "countPerUniverse": count_per_universe,
                "defaults": defaults,
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            UniverseThumbnails(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_group_icons(
            self,
            groups: List[GroupOrGroupId],
            size: SizeTupleOrString = (150, 150),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns icons for each group ID passed.
        Supported sizes:  
        - 150x150  
        - 420x420  

        Arguments:
            groups: Groups you want the thumbnails of.
            size: size of the image.
            image_format: Format of the image.
            is_circular: If the image is a circle yes or no.

        Returns:
            A list of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/groups/icons"),
            params={
                "groupIds": list(map(int, groups)),
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_place_icons(
            self,
            places: List[PlaceOrPlaceId],
            return_policy: ThumbnailReturnPolicy = ThumbnailReturnPolicy.place_holder,
            size: SizeTupleOrString = (50, 50),
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns icons for each place ID passed.
        Supported sizes:  
        - 50x50  
        - 128x128  
        - 150x150  
        - 256x256  
        - 512x512  
        - 768x432  

        Arguments:
            places: Places you want the thumbnails of.
            return_policy: How you want it returns look at enum.
            size: size of the image.
            image_format: Format of the image.
            is_circular: if the image is a circle yes or no.
        Returns:
            A List of Thumbnails.
        """
        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/places/gameicons"),
            params={
                "placeIds": list(map(int, places)),
                "returnPolicy": return_policy.value,
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )
        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_user_avatar_thumbnails(
            self,
            users: List[UserOrUserId],
            type: AvatarThumbnailType = AvatarThumbnailType.full_body,
            size: SizeTupleOrString = None,
            image_format: ThumbnailFormat = ThumbnailFormat.png,
            is_circular: bool = False,
    ) -> List[Thumbnail]:
        """
        Returns avatar thumbnails for each user ID passed.
        The valid sizes depend on the `type` parameter.

        | Size | full_body | headshot | bust |
        |---|---|---|---|
        | 30x30 | ✔️ | ❌ | ❌ |
        | 48x48 | ✔️ | ✔️ | ✔️ |
        | 50x50 | ❌ | ✔️ | ✔️ |
        | 60x60 | ✔️ | ✔️ | ✔️ |
        | 75x75 | ✔️ | ✔️ | ✔️ |
        | 100x100 | ✔️ | ✔️ | ✔️ |
        | 110x110 | ✔️ | ✔️ | ❌ |
        | 140x140 | ✔️ | ❌ | ❌ |
        | 150x150 | ✔️ | ✔️ | ✔️ |
        | 150x200 | ✔️ | ❌ | ❌ |
        | 180x180 | ✔️ | ✔️ | ✔️ |
        | 250x250 | ✔️ | ❌ | ❌ |
        | 352x352 | ✔️ | ✔️ | ✔️ |
        | 420x420 | ✔️ | ✔️ | ✔️ |
        | 720x720 | ✔️ | ❌ | ❌ |

        Arguments:
            users: Id of the users you want the thumbnails of.
            type: Type of avatar thumbnail you want look at enum.
            size: size of the image.
            image_format: Format of the image.
            is_circular: If the image is a circle yes or no.

        Returns:
            A list of Thumbnails.
        """
        uri: str
        if type == AvatarThumbnailType.full_body:
            uri = "avatar"
            size = size or (30, 30)
        elif type == AvatarThumbnailType.bust:
            uri = "avatar-bust"
            size = size or (48, 48)
        elif type == AvatarThumbnailType.headshot:
            uri = "avatar-headshot"
            size = size or (48, 48)
        else:
            raise ValueError("Avatar type is invalid.")

        thumbnails_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", f"v1/users/{uri}"),
            params={
                "userIds": list(map(int, users)),
                "size": _to_size_string(size),
                "format": image_format.value,
                "isCircular": is_circular,
            },
        )

        thumbnails_data = thumbnails_response.json()["data"]
        return [
            Thumbnail(client=self._client, data=thumbnail_data)
            for thumbnail_data in thumbnails_data
        ]

    async def get_user_avatar_thumbnail_3d(self, user: UserOrUserId) -> Thumbnail:
        """
        Returns the user's thumbnail in 3d.

        Arguments:
            user: User you want the 3d thumbnail of.

        Returns:
            A Thumbnail.
        """
        thumbnail_response = await self._client.requests.get(
            url=self._client.url_generator.get_url("thumbnails", "v1/users/avatar-3d"),
            params={
                "userId": int(user)
            },
        )
        thumbnail_data = thumbnail_response.json()
        return Thumbnail(client=self._client, data=thumbnail_data)
