import logging

from typing import (
    Dict,
    Optional,
    Tuple,
)

from devicemanager.model import (
    RequesterType,
)
from devicemanager.service import (
    DeviceStatusService,
    UsedServiceCache,
)
from .dto import GetUsedServiceRequest

logger = logging.getLogger(__name__)


class GetUsedServiceInteractor(object):
    def __init__(
        self,
        device_status_service,  # type: DeviceStatusService
        used_service_cache,     # type: UsedServiceCache
        sector_config,          # type: Dict[str, Dict[str, Tuple[str, Dict[int, int]]]]
        brand_config,           # type: Dict[str, Dict[str, str]]
    ):
        self.device_status_service = device_status_service
        self.used_service_cache = used_service_cache
        self.sector_config = sector_config
        self.brand_config = brand_config

    def get_used_service(
        self,       # type: GetUsedServiceInteractor
        request,    # type: GetUsedServiceRequest
    ):
        # type: (...) -> Optional[str]

        if request.device_type == "printer":
            logger.info("Getting printer device")
            return self._get_printer(request=request)

        return self._get_used_service_from_pos_model(request=request)

    def _get_printer(
        self,       # type: GetUsedServiceInteractor
        request,    # type: GetUsedServiceRequest
    ):
        # type: (...) -> Optional[str]

        preselected_printer = self._get_preselected_printer(request=request)
        if preselected_printer:
            return preselected_printer

        return self._get_requester_printer(request=request)

    def _get_preselected_printer(
        self,       # type: GetUsedServiceInteractor
        request,    # type: GetUsedServiceRequest
    ):
        # type: (...) -> Optional[str]

        brand_printer = self._get_brand_printer(
            brand=request.brand,
            brand_config=self.brand_config,
        )
        if brand_printer:
            logger.info("Retrieved brand printer: {}".format(brand_printer))
            return brand_printer

        sector_printer = self._get_sector_printer(
            sector=request.sector,
            sector_config=self.sector_config,
            pos_id=request.pos_id,
        )

        logger.info("Retrieved sector printer: {}".format(brand_printer))
        return sector_printer

    @staticmethod
    def _validate_key_config(
        key,            # type: str
        key_config,     # type: Dict
    ):
        # type: (...) -> Optional[str]

        if key is None:
            return None

        key = key.upper()
        if key not in key_config or u"PRINTER" not in key_config[key]:
            return None

        return key

    def _get_brand_printer(
        self,           # type: GetUsedServiceInteractor
        brand,          # type: str
        brand_config,   # type: Dict[str, Dict[str, str]]
    ):
        # type: (...) -> Optional[str]

        valid_brand = self._validate_key_config(
            key=brand,
            key_config=brand_config,
        )

        if not valid_brand:
            return None

        device_name = brand_config[valid_brand]["PRINTER"]

        if self.device_status_service.is_device_enabled(device_name=device_name):
            return device_name

        return None

    def _get_sector_printer(
        self,           # type: GetUsedServiceInteractor
        sector,         # type: str
        sector_config,  # type: Dict[str, Dict[str, Tuple[str, Dict[int, int]]]]
        pos_id,         # type: int
    ):
        # type: (...) -> Optional[str]

        valid_sector = self._validate_key_config(
            key=sector,
            key_config=sector_config,
        )

        if not valid_sector:
            return None

        device_name, excluded_pos = sector_config[valid_sector]["PRINTER"]
        if pos_id in excluded_pos:
            return None

        if self.device_status_service.is_device_enabled(device_name=device_name):
            return device_name

        return None

    def _get_requester_printer(
        self,       # type: GetUsedServiceInteractor
        request,    # type: GetUsedServiceRequest
    ):
        # type: (...) -> Optional[str]

        requester_type = RequesterType.POS if request.pos_id is not None else RequesterType.KDS
        requester_id = request.pos_id if request.pos_id is not None else request.kds_id
        logger.info("Getting printer from {}{}".format(requester_type, requester_id))
        return self.device_status_service.get_enabled_device(
            requester_type=requester_type,
            requester_id=requester_id
        )

    def _get_used_service_from_pos_model(
        self,       # type: GetUsedServiceInteractor
        request,    # type: GetUsedServiceRequest
    ):
        # type: (...) -> Optional[str]

        return self.used_service_cache.get_cached_object(
            device_type=request.device_type,
            pos_id=request.pos_id,
        )
