"""Account service."""

from typing import Any, Optional

from requests import Response

from pyicloud.services.base import BaseService
from pyicloud.session import PyiCloudSession
from pyicloud.utils import underscore_to_camelcase

DEFAULT_DSID = "20288408776"


class AccountService(BaseService):
    """The 'Account' iCloud service."""

    def __init__(
        self,
        service_root: str,
        session: PyiCloudSession,
        china_mainland: bool,
        params: dict[str, Any],
    ) -> None:
        super().__init__(service_root, session, params)

        self._devices: list["AccountDevice"] = []
        self._family: list["FamilyMember"] = []
        self._storage: Optional[AccountStorage] = None

        self._acc_endpoint: str = f"{self.service_root}/setup/web"
        self._acc_devices_url: str = f"{self._acc_endpoint}/device/getDevices"
        self._acc_family_details_url: str = (
            f"{self._acc_endpoint}/family/getFamilyDetails"
        )
        self._acc_family_member_photo_url: str = (
            f"{self._acc_endpoint}/family/getMemberPhoto"
        )
        self._acc_storage_url: str = f"{self.service_root}/setup/ws/1/storageUsageInfo"

        self._gateway: str = (
            f"https://gatewayws.icloud.com{'' if not china_mainland else '.cn'}"
        )
        self._gateway_root: str = f"{self._gateway}/acsegateway"
        dsid: str = self.params.get("dsid", DEFAULT_DSID)
        self._gateway_pricing_url: str = (
            f"{self._gateway_root}/v1/accounts/{dsid}/plans/icloud/pricing"
        )
        self._gateway_summary_plan_url: str = (
            f"{self._gateway_root}/v3/accounts/{dsid}/subscriptions"
            "/features/cloud.storage/plan-summary"
        )

    @property
    def devices(self) -> list["AccountDevice"]:
        """Returns current paired devices."""
        if not self._devices:
            req: Response = self.session.get(self._acc_devices_url, params=self.params)
            response = req.json()

            for device_info in response["devices"]:
                self._devices.append(AccountDevice(device_info))

        return self._devices

    @property
    def family(self) -> list["FamilyMember"]:
        """Returns family members."""
        if not self._family:
            req: Response = self.session.get(
                self._acc_family_details_url, params=self.params
            )
            response = req.json()

            for member_info in response.get("familyMembers", []):
                self._family.append(
                    FamilyMember(
                        member_info,
                        self.session,
                        self.params,
                        self._acc_family_member_photo_url,
                    )
                )

        return self._family

    @property
    def storage(self) -> "AccountStorage":
        """Returns storage infos."""
        if not self._storage:
            req: Response = self.session.post(self._acc_storage_url, params=self.params)
            response = req.json()

            self._storage = AccountStorage(response)

        return self._storage

    @property
    def summary_plan(self):
        """Returns your subscription plan."""
        req: Response = self.session.get(
            self._gateway_summary_plan_url, params=self.params
        )
        response = req.json()
        return response

    def __str__(self) -> str:
        return (
            f"{{devices: {len(self.devices)}, family: {len(self.family)}, "
            f"storage: {self.storage.usage.available_storage_in_bytes} bytes free}}"
        )

    def __repr__(self) -> str:
        return f"<{type(self).__name__}: {self}>"


class AccountDevice(dict):
    """Account device."""

    def __getattr__(self, key: str):
        return self[underscore_to_camelcase(key)]

    def __str__(self) -> str:
        return f"{{model: {self.model_display_name}, name: {self.name}}}"

    def __repr__(self) -> str:
        return f"<{type(self).__name__}: {self}>"


class FamilyMember:
    """A family member."""

    def __init__(
        self,
        member_info: dict[str, Any],
        session: PyiCloudSession,
        params: dict[str, Any],
        acc_family_member_photo_url: str,
    ) -> None:
        self._attrs: dict[str, Any] = member_info
        self._session: PyiCloudSession = session
        self._params: dict[str, Any] = params
        self._acc_family_member_photo_url: str = acc_family_member_photo_url

    @property
    def last_name(self) -> Optional[str]:
        """Gets the last name."""
        return self._attrs.get("lastName")

    @property
    def dsid(self) -> Optional[str]:
        """Gets the dsid."""
        return self._attrs.get("dsid")

    @property
    def original_invitation_email(self) -> Optional[str]:
        """Gets the original invitation."""
        return self._attrs.get("originalInvitationEmail")

    @property
    def full_name(self) -> Optional[str]:
        """Gets the full name."""
        return self._attrs.get("fullName")

    @property
    def age_classification(self):
        """Gets the age classification."""
        return self._attrs.get("ageClassification")

    @property
    def apple_id_for_purchases(self) -> Optional[str]:
        """Gets the apple id for purchases."""
        return self._attrs.get("appleIdForPurchases")

    @property
    def apple_id(self) -> Optional[str]:
        """Gets the apple id."""
        return self._attrs.get("appleId")

    @property
    def family_id(self):
        """Gets the family id."""
        return self._attrs.get("familyId")

    @property
    def first_name(self) -> Optional[str]:
        """Gets the first name."""
        return self._attrs.get("firstName")

    @property
    def has_parental_privileges(self):
        """Has parental privileges."""
        return self._attrs.get("hasParentalPrivileges")

    @property
    def has_screen_time_enabled(self):
        """Has screen time enabled."""
        return self._attrs.get("hasScreenTimeEnabled")

    @property
    def has_ask_to_buy_enabled(self):
        """Has to ask for buying."""
        return self._attrs.get("hasAskToBuyEnabled")

    @property
    def has_share_purchases_enabled(self):
        """Has share purshases."""
        return self._attrs.get("hasSharePurchasesEnabled")

    @property
    def share_my_location_enabled_family_members(self):
        """Has share my location with family."""
        return self._attrs.get("shareMyLocationEnabledFamilyMembers")

    @property
    def has_share_my_location_enabled(self):
        """Has share my location."""
        return self._attrs.get("hasShareMyLocationEnabled")

    @property
    def dsid_for_purchases(self):
        """Gets the dsid for purchases."""
        return self._attrs.get("dsidForPurchases")

    def get_photo(self) -> Response:
        """Returns the photo."""
        params_photo = dict(self._params)
        params_photo.update({"memberId": self.dsid})
        return self._session.get(
            self._acc_family_member_photo_url, params=params_photo, stream=True
        )

    def __getitem__(self, key):
        if self._attrs.get(key):
            return self._attrs[key]
        return getattr(self, key)

    def __str__(self) -> str:
        return (
            f"{{name: {self.full_name}, age_classification: {self.age_classification}}}"
        )

    def __repr__(self) -> str:
        return f"<{type(self).__name__}: {self}>"


class AccountStorageUsageForMedia:
    """Storage used for a specific media type into the account."""

    def __init__(self, usage_data) -> None:
        self.usage_data: dict[str, Any] = usage_data

    @property
    def key(self):
        """Gets the key."""
        return self.usage_data["mediaKey"]

    @property
    def label(self):
        """Gets the label."""
        return self.usage_data["displayLabel"]

    @property
    def color(self):
        """Gets the HEX color."""
        return self.usage_data["displayColor"]

    @property
    def usage_in_bytes(self):
        """Gets the usage in bytes."""
        return self.usage_data["usageInBytes"]

    def __str__(self) -> str:
        return f"{{key: {self.key}, usage: {self.usage_in_bytes} bytes}}"

    def __repr__(self) -> str:
        return f"<{type(self).__name__}: {self}>"


class AccountStorageUsage:
    """Storage used for a specific media type into the account."""

    def __init__(self, usage_data, quota_data) -> None:
        self.usage_data: dict[str, Any] = usage_data
        self.quota_data: dict[str, Any] = quota_data

    @property
    def comp_storage_in_bytes(self):
        """Gets the comp storage in bytes."""
        return self.usage_data["compStorageInBytes"]

    @property
    def used_storage_in_bytes(self):
        """Gets the used storage in bytes."""
        return self.usage_data["usedStorageInBytes"]

    @property
    def used_storage_in_percent(self):
        """Gets the used storage in percent."""
        return round(self.used_storage_in_bytes * 100 / self.total_storage_in_bytes, 2)

    @property
    def available_storage_in_bytes(self):
        """Gets the available storage in bytes."""
        return self.total_storage_in_bytes - self.used_storage_in_bytes

    @property
    def available_storage_in_percent(self):
        """Gets the available storage in percent."""
        return round(
            self.available_storage_in_bytes * 100 / self.total_storage_in_bytes, 2
        )

    @property
    def total_storage_in_bytes(self):
        """Gets the total storage in bytes."""
        return self.usage_data["totalStorageInBytes"]

    @property
    def commerce_storage_in_bytes(self):
        """Gets the commerce storage in bytes."""
        return self.usage_data["commerceStorageInBytes"]

    @property
    def quota_over(self):
        """Gets the over quota."""
        return self.quota_data["overQuota"]

    @property
    def quota_tier_max(self):
        """Gets the max tier quota."""
        return self.quota_data["haveMaxQuotaTier"]

    @property
    def quota_almost_full(self):
        """Gets the almost full quota."""
        return self.quota_data["almost-full"]

    @property
    def quota_paid(self):
        """Gets the paid quota."""
        return self.quota_data["paidQuota"]

    def __str__(self) -> str:
        return f"{self.used_storage_in_percent}% used of {self.total_storage_in_bytes} bytes"

    def __repr__(self) -> str:
        return f"<{type(self).__name__}: {self}>"


class AccountStorage:
    """Storage of the account."""

    def __init__(self, storage_data) -> None:
        self.usage = AccountStorageUsage(
            storage_data.get("storageUsageInfo"), storage_data.get("quotaStatus")
        )
        self.usages_by_media: dict[str, AccountStorageUsageForMedia] = {}

        for usage_media in storage_data.get("storageUsageByMedia"):
            self.usages_by_media[usage_media["mediaKey"]] = AccountStorageUsageForMedia(
                usage_media
            )

    def __str__(self) -> str:
        return f"{{usage: {self.usage}, usages_by_media: {self.usages_by_media}}}"

    def __repr__(self) -> str:
        return f"<{type(self).__name__}: {self}>"
