import json
import random
import time
from pathlib import Path
from typing import Dict, List
from urllib.parse import urlparse

from aiograpi import config
from aiograpi.exceptions import HighlightNotFound
from aiograpi.extractors import extract_highlight_v1
from aiograpi.types import Highlight
from aiograpi.utils import dumps


class HighlightMixin:
    async def highlight_pk_from_url(self, url: str) -> str:
        """
        Get Highlight PK from URL

        Parameters
        ----------
        url: str
            URL of highlight

        Returns
        -------
        str
            Highlight PK

        Examples
        --------
        https://www.instagram.com/stories/highlights/17895485201104054/ -> 17895485201104054
        """
        assert "/highlights/" in url, 'URL must contain "/highlights/"'
        path = urlparse(url).path
        parts = [p for p in path.split("/") if p and p.isdigit()]
        return str(parts[0])

    async def user_highlights_v1(
        self, user_id: int, amount: int = 0
    ) -> List[Highlight]:
        """
        Get a user's highlight

        Parameters
        ----------
        user_id: int
        amount: int, optional
            Maximum number of highlight to return, default is 0 (all highlights)

        Returns
        -------
        List[Highlight]
            A list of objects of Highlight
        """
        amount = int(amount)
        user_id = int(user_id)
        params = {
            "supported_capabilities_new": json.dumps(config.SUPPORTED_CAPABILITIES),
            "phone_id": self.phone_id,
            "battery_level": random.randint(25, 100),
            "is_charging": random.randint(0, 1),
            "will_sound_on": random.randint(0, 1),
        }
        result = await self.private_request(
            f"highlights/{user_id}/highlights_tray/", params=params
        )
        return [extract_highlight_v1(highlight) for highlight in result.get("tray", [])]

    async def user_highlights(self, user_id: int, amount: int = 0) -> List[Highlight]:
        """
        Get a user's highlights

        Parameters
        ----------
        user_id: int
        amount: int, optional
            Maximum number of highlight to return, default is 0 (all highlights)

        Returns
        -------
        List[Highlight]
            A list of objects of Highlight
        """
        return await self.user_highlights_v1(user_id, amount)

    async def highlight_info_v1(self, highlight_pk: str) -> Highlight:
        """
        Get Highlight by pk or id (by Private Mobile API)

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight

        Returns
        -------
        Highlight
            An object of Highlight type
        """
        highlight_id = f"highlight:{highlight_pk}"
        data = {
            "exclude_media_ids": "[]",
            "supported_capabilities_new": json.dumps(config.SUPPORTED_CAPABILITIES),
            "source": "profile",
            "_uid": str(self.user_id),
            "_uuid": self.uuid,
            "user_ids": [highlight_id],
        }
        result = await self.private_request("feed/reels_media/", data)
        data = result["reels"]
        if str(highlight_pk) not in (k.split(":")[-1] for k in data.keys()):
            raise HighlightNotFound(highlight_pk=highlight_pk, **data)
        item = next(item for item in data.values() if isinstance(item, dict))
        return extract_highlight_v1(item)

    async def highlight_info(self, highlight_pk: str) -> Highlight:
        """
        Get Highlight by pk or id

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight

        Returns
        -------
        Highlight
            An object of Highlight type
        """
        return await self.highlight_info_v1(highlight_pk)

    async def highlight_create(
        self,
        title: str,
        story_ids: List[str],
        cover_story_id: str = "",
        crop_rect: List[float] = [0.0, 0.21830457, 1.0, 0.78094524],
    ) -> Highlight:
        """
        Create highlight

        Parameters
        ----------
        title: str
            Title
        story_ids: List[str]
            List of story ids
        cover_story_id: str
            User story as cover, default is first of story_ids

        Returns
        -------
        Highlight
            An object of Highlight type
        """
        if not cover_story_id:
            cover_story_id = story_ids[0]
        data = {
            "supported_capabilities_new": json.dumps(config.SUPPORTED_CAPABILITIES),
            "source": "self_profile",
            "creation_id": str(int(time.time())),
            "_uid": str(self.user_id),
            "_uuid": self.uuid,
            "cover": dumps(
                {
                    "media_id": await self.media_id(cover_story_id),
                    "crop_rect": dumps(crop_rect),
                }
            ),
            "title": title,
            "media_ids": dumps([await self.media_id(sid) for sid in story_ids]),
        }
        result = await self.private_request("highlights/create_reel/", data=data)
        return extract_highlight_v1(result["reel"])

    async def highlight_edit(
        self,
        highlight_pk: str,
        title: str = "",
        cover: Dict = {},
        added_media_ids: List[str] = [],
        removed_media_ids: List[str] = [],
    ):
        data = {
            "supported_capabilities_new": json.dumps(config.SUPPORTED_CAPABILITIES),
            "source": "self_profile",
            "_uid": str(self.user_id),
            "_uuid": self.uuid,
            "added_media_ids": dumps(added_media_ids),
            "removed_media_ids": dumps(removed_media_ids),
        }
        if title:
            data["title"] = title
        if cover:
            data["cover"] = dumps(cover)
        result = await self.private_request(
            f"highlights/highlight:{highlight_pk}/edit_reel/", data=data
        )
        return extract_highlight_v1(result["reel"])

    async def highlight_change_title(self, highlight_pk: str, title: str) -> Highlight:
        """
        Change title for highlight

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight
        title: str
            Title of Highlight

        Returns
        -------
        Highlight
        """
        return await self.highlight_edit(highlight_pk, title=title)

    async def highlight_change_cover(
        self, highlight_pk: str, cover_path: Path
    ) -> Highlight:
        """
        Change cover for highlight

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight
        cover_path: Path
            Path to photo

        Returns
        -------
        Highlight
        """
        upload_id, width, height = await self.photo_rupload(Path(cover_path))
        cover = {"upload_id": str(upload_id), "crop_rect": "[0.0,0.0,1.0,1.0]"}
        return await self.highlight_edit(highlight_pk, cover=cover)

    async def highlight_add_stories(
        self, highlight_pk: str, added_media_ids: List[str]
    ) -> Highlight:
        """
        Add stories to highlight

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight
        removed_media_ids: List[str]
            Remove stories from highlight

        Returns
        -------
        Highlight
        """
        return await self.highlight_edit(highlight_pk, added_media_ids=added_media_ids)

    async def highlight_remove_stories(
        self, highlight_pk: str, removed_media_ids: List[str]
    ) -> Highlight:
        """
        Remove stories from highlight

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight
        removed_media_ids: List[str]
            Remove stories from highlight

        Returns
        -------
        Highlight
        """
        return self.highlight_edit(highlight_pk, removed_media_ids=removed_media_ids)

    async def highlight_delete(self, highlight_pk: str) -> bool:
        """
        Delete highlight

        Parameters
        ----------
        highlight_pk: str
            Unique identifier of Highlight

        Returns
        -------
        bool
        """
        data = {"_uid": str(self.user_id), "_uuid": self.uuid}
        result = await self.private_request(
            f"highlights/highlight:{highlight_pk}/delete_reel/", data=data
        )
        return result.get("status") == "ok"
