import logging
import typing

from .device_service import SHCDeviceService
from .exceptions import SHCException
from .services_impl import SUPPORTED_DEVICE_SERVICE_IDS, build

logger = logging.getLogger("boschshcpy")


class SHCDevice:
    def __init__(self, api, raw_device, raw_device_services):
        self._api = api
        self._raw_device = raw_device

        self._callbacks = {}
        self._device_services_by_id = {}
        if not raw_device_services:
            self._enumerate_services()
        else:
            self._init_services(raw_device_services)

    def _enumerate_services(self):
        for device_service_id in self._raw_device["deviceServiceIds"]:
            if device_service_id not in SUPPORTED_DEVICE_SERVICE_IDS:
                continue

            raw_device_service_data = self._api.get_device_service(
                self.id, device_service_id
            )
            device_service = build(self._api, raw_device_service_data)

            self._device_services_by_id[device_service_id] = device_service

    def _init_services(self, raw_device_services):
        for raw_device_service_data in raw_device_services:
            device_service = build(self._api, raw_device_service_data)
            self._device_services_by_id[raw_device_service_data["id"]] = device_service

    @property
    def root_device_id(self):
        return self._raw_device["rootDeviceId"]

    @property
    def id(self):
        return self._raw_device["id"]

    @property
    def manufacturer(self):
        return self._raw_device["manufacturer"]

    @property
    def room_id(self):
        return self._raw_device["roomId"] if "roomId" in self._raw_device else None

    @property
    def device_model(self):
        return self._raw_device["deviceModel"]

    @property
    def serial(self):
        return self._raw_device["serial"] if "serial" in self._raw_device else None

    @property
    def profile(self):
        return self._raw_device["profile"] if "profile" in self._raw_device else None

    @property
    def name(self):
        return self._raw_device["name"]

    @property
    def status(self):
        return self._raw_device["status"]

    @property
    def deleted(self):
        return (
            True
            if "deleted" in self._raw_device and self._raw_device["deleted"] == True
            else False
        )

    @property
    def child_device_ids(self):
        return (
            self._raw_device["childDeviceIds"]
            if "childDeviceIds" in self._raw_device
            else None
        )

    @property
    def parent_device_id(self):
        return (
            self._raw_device["parentDeviceId"]
            if "parentDeviceId" in self._raw_device
            else None
        )

    @property
    def device_services(self) -> typing.Sequence[SHCDeviceService]:
        return list(self._device_services_by_id.values())

    @property
    def device_service_ids(self) -> typing.Set[str]:
        return set(self._device_services_by_id.keys())

    def subscribe_callback(self, entity, callback):
        self._callbacks[entity] = callback

    def unsubscribe_callback(self, entity):
        self._callbacks.pop(entity, None)

    def update_raw_information(self, raw_device):
        if self._raw_device["id"] != raw_device["id"]:
            raise SHCException("Error due to mismatching device ids!")
        self._raw_device = raw_device

        for callback in self._callbacks:
            self._callbacks[callback]()

    def device_service(self, device_service_id):
        return (
            self._device_services_by_id[device_service_id]
            if device_service_id in self._device_services_by_id
            else None
        )

    def summary(self):
        print(f"Device: {self.id}")
        print(f"  Name          : {self.name}")
        print(f"  Manufacturer  : {self.manufacturer}")
        print(f"  Model         : {self.device_model}")
        print(f"  Room          : {self.room_id}")
        print(f"  Serial        : {self.serial}")
        print(f"  Profile       : {self.profile}")
        print(f"  Status        : {self.status}")
        print(f"  ParentDevice  : {self.parent_device_id}")
        print(f"  ChildDevices  : {self.child_device_ids}")
        print(f"  DeviceServices: {self._raw_device['deviceServiceIds']}")
        for device_service in self.device_services:
            device_service.summary()

    def process_long_polling_poll_result(self, raw_result):
        assert raw_result["@type"] == "DeviceServiceData"
        device_service_id = raw_result["id"]
        if device_service_id in self._device_services_by_id.keys():
            device_service = self._device_services_by_id[device_service_id]
            device_service.process_long_polling_poll_result(raw_result)
        else:
            logger.debug(
                f"Skipping polling result with unknown device service id {device_service_id}."
            )
