from typing import (
    List,
    Dict,
    Optional,
)

from devicemanager.model import (
    RequesterType,
    DeviceConfig,
)
from devicemanager.repository import DeviceManagerRepository


class DeviceStatusService(object):
    def __init__(
        self,
        device_configs,  # type: List[DeviceConfig]
    ):
        # type: (...) -> None

        self.device_configs = device_configs
        self.requester_current_device = None        # type: Optional[Dict[str, str]]
        self.status_dict = None                     # type: Optional[Dict[str, bool]]

    def get_enabled_device(
        self,               # type: DeviceStatusService
        requester_type,     # type: RequesterType
        requester_id,       # type: int
    ):
        key = self._get_requester_key(
            requester_type=requester_type.name,
            requester_id=requester_id,
        )
        return self.requester_current_device.get(key)

    def is_device_enabled(
        self,           # type: DeviceStatusService
        device_name,    # type: str
    ):
        # type: (...) -> bool

        if device_name in self.status_dict:
            return self.status_dict[device_name]
        return True

    def initialize_cache(
        self,        # type: DeviceStatusService
        repository,  # type: DeviceManagerRepository
    ):
        # type: (...) -> None

        self.status_dict = self._build_device_status_dict(
            repository=repository,
            device_configs=self.device_configs,
        )
        self.requester_current_device = self._build_requester_current_device(
            device_configs=self.device_configs,
            status_dict=self.status_dict,
        )

    def _build_requester_current_device(
        self,
        device_configs,  # type: List[DeviceConfig]
        status_dict,     # type: Dict[str, bool]
    ):
        # type: (...) -> Dict[str, str]

        requester_current_device = {}
        for config in device_configs:
            turned_on_device = self._get_turned_on_device(
                config=config,
                status_dict=status_dict,
            )
            key = self._get_requester_key(
                requester_type=config.requester_type,
                requester_id=config.requester_id,
            )
            requester_current_device[key] = turned_on_device

        return requester_current_device

    def _build_device_status_dict(
        self,            # type: DeviceStatusService
        repository,      # type: DeviceManagerRepository
        device_configs,  # type: List[DeviceConfig]
    ):
        # type: (...) -> Dict[str, bool]

        status_dict = self._build_status_dict_with_disabled_devices(repository=repository)
        self._add_all_devices_to_status_dict(
            device_configs=device_configs,
            status_dict=status_dict,
        )
        return status_dict

    @staticmethod
    def _build_status_dict_with_disabled_devices(
        repository,     # type: DeviceManagerRepository
    ):
        # type: (...) -> Dict[str, bool]

        ret = {}
        device_statuses = repository.get_disabled_devices()
        for device_name in device_statuses.keys():
            device = device_statuses[device_name]
            ret[device_name] = device.enabled
        return ret

    def _add_all_devices_to_status_dict(
        self,            # type: DeviceStatusService
        device_configs,  # type: List[DeviceConfig]
        status_dict,     # type: Dict[str, bool]
    ):
        # type: (...) -> None

        for device_config in device_configs:
            self._add_configured_devices_to_status_dict(
                config=device_config,
                status_dict=status_dict,
            )

    @staticmethod
    def _add_configured_devices_to_status_dict(
        config,         # type: DeviceConfig
        status_dict,    # type: Dict[str, bool]
    ):
        # type: (...) -> None

        device_order = [config.default_device] + config.fallback_devices
        for device_name in device_order:
            if device_name not in status_dict:
                status_dict[device_name] = True

    @staticmethod
    def _get_requester_key(
        requester_type,     # type: str
        requester_id,       # type: int
    ):
        # type: (...) -> str

        return str(requester_type) + str(requester_id)

    @staticmethod
    def _get_turned_on_device(
        config,       # type: DeviceConfig
        status_dict,  # type: Dict[str, bool]
    ):
        # type: (...) -> Optional[str]

        device_order = [config.default_device] + config.fallback_devices
        for device in device_order:
            if status_dict[device]:
                return device
        return None
