import json
from enum import Enum
from typing import Dict, List, Any

from elmax_api.model.actuator import Actuator
from elmax_api.model.area import Area
from elmax_api.model.cover import Cover
from elmax_api.model.endpoint import DeviceEndpoint
from elmax_api.model.goup import Group
from elmax_api.model.scene import Scene
from elmax_api.model.zone import Zone


class PanelEntry:
    """Representation of an available control panel."""

    def __init__(self, devicehash: str, online: bool, name_by_user: Dict[str, str]):
        """Initialize the new control panel."""
        self._hash = devicehash
        self._online = online
        self._names = name_by_user

    @property
    def hash(self) -> str:
        return self._hash

    @property
    def online(self) -> bool:
        return self._online

    def get_name_by_user(self, username: str) -> str:
        if username not in self._names:
            ValueError(
                "Cannot find the name associated by user %s to device %s",
                username,
                self._hash,
            )
        return self._names.get(username)

    @staticmethod
    def from_api_response(response_entry: Dict) -> 'PanelEntry':
        """Create a new control panel from the API json response"""
        # Convert the data structure so that we have a dictionary of names by user
        name_by_user = dict()
        for entry in response_entry.get("username", []):
            username = entry.get("name")
            name = entry.get("label")
            name_by_user[username] = name

        control_panel = PanelEntry(
            devicehash=response_entry["hash"],
            online=bool(response_entry["centrale_online"]),
            name_by_user=name_by_user,
        )
        return control_panel


class PanelStatus:
    """Representation of a panel status"""

    def __init__(self,
                 panel_id: str,
                 user_email: str,
                 release: str,
                 cover_feature: bool,
                 scene_feature: bool,
                 zones: List[Zone],
                 actuators: List[Actuator],
                 areas: List[Area],
                 groups: List[Group],
                 scenes: List[Scene],
                 covers: List[Cover],
                 push_feature: bool,
                 accessory_type: str,
                 accessory_release: str,
                 *args,
                 **kwargs
                 ):

        self._panel_id = panel_id
        self._user_email = user_email
        self._release = release
        self._cover_feature = cover_feature
        self._scene_feature = scene_feature
        self._zones = zones
        self._actuators = actuators
        self._areas = areas
        self._groups = groups
        self._scenes = scenes
        self._covers = covers
        self._push_feature = push_feature
        self._accessory_type = accessory_type
        self._accessory_release = accessory_release

    @property
    def panel_id(self) -> str:
        return self._panel_id

    @property
    def user_email(self) -> str:
        return self._user_email

    @property
    def release(self) -> str:
        return self._release

    @property
    def cover_feature(self) -> bool:
        return self._cover_feature

    @property
    def scene_feature(self) -> bool:
        return self._scene_feature

    @property
    def zones(self) -> List[Zone]:
        return self._zones

    @property
    def actuators(self) -> List[Actuator]:
        return self._actuators

    @property
    def areas(self) -> List[Area]:
        return self._areas

    @property
    def groups(self) -> List[Group]:
        return self._groups

    @property
    def scenes(self) -> List[Scene]:
        return self._scenes

    @property
    def covers(self) -> List[Cover]:
        return self._covers

    @property
    def all_endpoints(self) -> List[DeviceEndpoint]:
        res = []
        res.extend(self.actuators)
        res.extend(self.areas)
        res.extend(self.groups)
        res.extend(self.scenes)
        res.extend(self.zones)
        res.extend(self.covers)
        return res

    @property
    def push_feature(self) -> bool:
        return self._push_feature

    @property
    def accessory_type(self) -> str:
        return self._accessory_type

    @property
    def accessory_release(self) -> str:
        return self._accessory_release

    def __repr__(self):
        def inspectobj(obj):
            if isinstance(obj,Enum):
                return obj.name
            elif hasattr(obj, "__dict__"):
                return vars(obj)
            else:
                return str(obj)

        return json.dumps(self, default=inspectobj)

    @staticmethod
    def from_api_response(response_entry: Dict) -> 'PanelStatus':
        """Create a new panel status object from the API json response"""
        panel_status = PanelStatus(
            panel_id=response_entry.get('centrale'),
            user_email=response_entry.get('utente'),
            release=response_entry.get('release'),
            cover_feature=response_entry.get('tappFeature'),
            scene_feature=response_entry.get('sceneFeature'),
            push_feature=response_entry.get('pushFeature', False),
            accessory_type=response_entry.get('tipo_accessorio', 'Unknown'),
            accessory_release=response_entry.get('release_accessorio', 'Unknown'),
            zones=[Zone.from_api_response(x) for x in response_entry.get('zone', [])],
            actuators=[Actuator.from_api_response(x) for x in response_entry.get('uscite', [])],
            areas=[Area.from_api_response(x) for x in response_entry.get('aree', [])],
            covers=[Cover.from_api_response(x) for x in response_entry.get('tapparelle', [])],
            groups=[Group.from_api_response(x) for x in response_entry.get('gruppi', [])],
            scenes=[Scene.from_api_response(x) for x in response_entry.get('scenari', [])]
        )
        return panel_status

class EndpointStatus:
    """Representation of an endpoint status"""

    def __init__(self,
                 release: str,
                 cover_feature: bool,
                 scene_feature: bool,
                 zones: List[Zone],
                 actuators: List[Actuator],
                 areas: List[Area],
                 groups: List[Group],
                 scenes: List[Scene],
                 covers: List[Cover],
                 push_feature: bool,
                 accessory_type: str,
                 accessory_release: str,
                 *args,
                 **kwargs):

        self._release = release
        self._cover_feature = cover_feature
        self._scene_feature = scene_feature
        self._zones = zones
        self._actuators = actuators
        self._areas = areas
        self._groups = groups
        self._scenes = scenes
        self._covers = covers
        self._push_feature = push_feature
        self._accessory_type = accessory_type
        self._accessory_release = accessory_release

    @property
    def release(self) -> str:
        return self._release

    @property
    def cover_feature(self) -> bool:
        return self._cover_feature

    @property
    def scene_feature(self) -> bool:
        return self._scene_feature

    @property
    def zones(self) -> List[Zone]:
        return self._zones

    @property
    def actuators(self) -> List[Actuator]:
        return self._actuators

    @property
    def areas(self) -> List[Area]:
        return self._areas

    @property
    def groups(self) -> List[Group]:
        return self._groups

    @property
    def scenes(self) -> List[Scene]:
        return self._scenes

    @property
    def covers(self) -> List[Cover]:
        return self._covers

    @property
    def all_endpoints(self) -> List[DeviceEndpoint]:
        res = []
        res.extend(self.actuators)
        res.extend(self.areas)
        res.extend(self.groups)
        res.extend(self.scenes)
        res.extend(self.zones)
        return res

    @property
    def push_feature(self) -> bool:
        return self._push_feature

    @property
    def accessory_type(self) -> str:
        return self._accessory_type

    @property
    def accessory_release(self) -> str:
        return self._accessory_release

    @staticmethod
    def from_api_response(response_entry: Dict) -> 'EndpointStatus':
        """Create a new endpoint status object from the API json response"""
        status = EndpointStatus(
            release=response_entry.get('release', 'Unknown'),
            cover_feature=response_entry.get('tappFeature', False),
            scene_feature=response_entry.get('sceneFeature', False),
            push_feature=response_entry.get('pushFeature', False),
            accessory_type=response_entry.get('tipo_accessorio', 'Unknown'),
            accessory_release=response_entry.get('release_accessorio', 'Unknown'),
            zones=[Zone.from_api_response(x) for x in response_entry.get('zone', [])],
            actuators=[Actuator.from_api_response(x) for x in response_entry.get('uscite', [])],
            areas=[Area.from_api_response(x) for x in response_entry.get('aree', [])],
            covers=[Cover.from_api_response(x) for x in response_entry.get('tapparelle', [])],
            groups=[Group.from_api_response(x) for x in response_entry.get('gruppi', [])],
            scenes=[Scene.from_api_response(x) for x in response_entry.get('scenari', [])]
        )
        return status