"""Define the subscription module."""

from enum import Enum
from typing import Any, Optional

from .api import Api
from .entity import Entity


class SourceType(Enum):
    """Define the source type of a subscription."""

    UNKNOWN = "UNKNOWN"
    DEVICE = "DEVICE"
    CAPABILITY = "CAPABILITY"


class Subscription:
    """Define the subscription class."""

    def __init__(self):
        """Initialize a new instance of the subscription class."""
        self._subscription_id = None
        self._installed_app_id = None
        self._source_type = SourceType.UNKNOWN
        self._capability = "*"
        self._attribute = "*"
        self._value = "*"
        self._state_change_only = True
        self._subscription_name = None
        # Capability-specific attributes
        self._location_id = None
        # Device-specific attributes
        self._device_id = None
        self._component_id = None

    def apply_data(self, data: dict):
        """Set the states of the app with the supplied data."""
        self._subscription_id = data["id"]
        self._installed_app_id = data["installedAppId"]
        self._source_type = SourceType(data["sourceType"])
        if self._source_type is SourceType.CAPABILITY:
            capability = data["capability"]
            self._location_id = capability["locationId"]
            self._capability = capability["capability"]
            self._attribute = capability.get("attribute", "*")
            self._value = capability.get("value", "*")
            self._state_change_only = capability.get("stateChangeOnly", True)
            self._subscription_name = capability.get("subscriptionName", None)
        if self._source_type is SourceType.DEVICE:
            device = data["device"]
            self._device_id = device["deviceId"]
            self._component_id = device.get("componentId", "*")
            self._capability = device.get("capability", "*")
            self._attribute = device.get("attribute", "*")
            self._value = device.get("value", "*")
            self._state_change_only = device.get("stateChangeOnly", True)
            self._subscription_name = device.get("subscriptionName", None)

    def to_data(self) -> dict:
        """Get a data structure representing this entity."""
        data = {"sourceType": self._source_type.value}
        if self._source_type is SourceType.CAPABILITY:
            capability = {
                "locationId": self._location_id,
                "capability": self._capability,
            }
            if self._attribute and self._attribute != "*":
                capability["attribute"] = self._attribute
            if self._value and self._value != "*":
                capability["value"] = self._value
            if not self._state_change_only:
                capability["stateChangeOnly"] = False
            if self._subscription_name:
                capability["subscriptionName"] = self._subscription_name
            data["capability"] = capability
        if self._source_type is SourceType.DEVICE:
            device = {
                "deviceId": self._device_id,
                "stateChangeOnly": self._state_change_only,
            }
            if self._component_id and self._component_id != "*":
                device["componentId"] = self._component_id
            if self._capability and self._capability != "*":
                device["capability"] = self._capability
            if self._attribute and self._attribute != "*":
                device["attribute"] = self._attribute
            if self._value and self._value != "*":
                device["value"] = self._value
            if self._subscription_name:
                device["subscriptionName"] = self._subscription_name
            data["device"] = device
        return data

    @property
    def subscription_id(self) -> str:
        """Get the id of the subscription."""
        return self._subscription_id

    @property
    def installed_app_id(self) -> str:
        """Get the id of the subscribing app."""
        return self._installed_app_id

    @installed_app_id.setter
    def installed_app_id(self, value: str):
        """Set the id of the subscripting app."""
        self._installed_app_id = value

    @property
    def source_type(self) -> SourceType:
        """Get the type of the event that is being subscribed to."""
        return self._source_type

    @source_type.setter
    def source_type(self, value: Any):
        """Set the typ eof event that is being subscribed to."""
        self._source_type = SourceType(value)

    @property
    def capability(self) -> str:
        """Get the name of the capability that is subscribed."""
        return self._capability

    @capability.setter
    def capability(self, value: str):
        """Get the name of the capability that is subscribed."""
        self._capability = value

    @property
    def attribute(self) -> str:
        """Get the name of the capabilities attribute or * for all."""
        return self._attribute

    @attribute.setter
    def attribute(self, value: str):
        """Set the name of the capabilities attribute or * for all."""
        self._attribute = value

    @property
    def value(self) -> str:
        """Get the value for that will trigger the subscription."""
        return self._value

    @value.setter
    def value(self, value: str):
        """Set the value for that will trigger the subscription."""
        self._value = value

    @property
    def state_change_only(self) -> bool:
        """Get to execute only on a state change."""
        return self._state_change_only

    @state_change_only.setter
    def state_change_only(self, value: bool):
        """Set to execute only on a state change."""
        self._state_change_only = value

    @property
    def subscription_name(self) -> str:
        """Get a name for the subscription."""
        return self._subscription_name

    @subscription_name.setter
    def subscription_name(self, value: str):
        """Set a name for the subscription."""
        self._subscription_name = value

    @property
    def location_id(self) -> str:
        """Get the location id that both the app and source device are in."""
        return self._location_id

    @location_id.setter
    def location_id(self, value: str):
        """Set the location id that both the app and source device are in."""
        self._location_id = value

    @property
    def device_id(self):
        """Get the GUID of the device that is subscribed to."""
        return self._device_id

    @device_id.setter
    def device_id(self, value: str):
        """Set the GUID of the device that is subscribed to."""
        self._device_id = value

    @property
    def component_id(self) -> str:
        """Get the component ID on the device that is subscribed to."""
        return self._component_id

    @component_id.setter
    def component_id(self, value: str):
        """Set the component ID on the device that is subscribed to."""
        self._component_id = value


class SubscriptionEntity(Entity, Subscription):
    """Define a subscription entity."""

    def __init__(self, api: Api, data: Optional[dict] = None):
        """Create a new instance of the SubscriptionEntity class."""
        Entity.__init__(self, api)
        Subscription.__init__(self)
        if data:
            self.apply_data(data)

    async def refresh(self):
        """Refresh the subscription information using the API."""
        data = await self._api.get_subscription(
            self._installed_app_id, self._subscription_id
        )
        self.apply_data(data)

    async def save(self):
        """Subscriptions cannot be updated."""
        raise NotImplementedError
