#     Copyright 2021. FastyBird s.r.o.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.

"""
Shelly connector plugin client
"""

# Python base dependencies
import uuid
from typing import Optional, Set

# Library libs
from miniserver_gateway.connectors.shelly.clients.base import IClient
from miniserver_gateway.connectors.shelly.clients.coap import CoapClient
from miniserver_gateway.connectors.shelly.clients.http import HttpClient
from miniserver_gateway.connectors.shelly.clients.mdns import MdnsClient
from miniserver_gateway.connectors.shelly.exceptions import InvalidArgumentException
from miniserver_gateway.connectors.shelly.logger import Logger
from miniserver_gateway.connectors.shelly.receivers.receiver import Receiver
from miniserver_gateway.connectors.shelly.registry.model import (
    AttributesRegistry,
    BlocksRegistry,
    CommandsRegistry,
    DevicesRegistry,
)
from miniserver_gateway.connectors.shelly.registry.records import SensorRecord
from miniserver_gateway.connectors.shelly.types import ClientType


class Client:
    """
    Plugin clients proxy

    @package        FastyBird:ShellyConnectorPlugin!
    @module         client

    @author         Adam Kadlec <adam.kadlec@fastybird.com>
    """

    __clients: Set[IClient] = set()

    # -----------------------------------------------------------------------------

    def __init__(self) -> None:
        self.__clients = set()

    # -----------------------------------------------------------------------------

    def register_client(self, client: IClient) -> None:
        """Append new client"""
        self.__clients.add(client)

    # -----------------------------------------------------------------------------

    def remove_client(self, client_id: uuid.UUID) -> bool:
        """Remove client from clients"""
        for client in self.__clients:
            if client_id.__eq__(client.id):
                self.__clients.remove(client)

        return False

    # -----------------------------------------------------------------------------

    def reset_clients(self) -> None:
        """Reset registered clients"""
        self.__clients = set()

    # -----------------------------------------------------------------------------

    def enable_client(self, client_id: Optional[uuid.UUID] = None) -> bool:
        """Enable one or more clients"""
        result = False

        for client in self.__clients:
            if client_id is None or client_id.__eq__(client.id):
                result = client.enable()

        return result

    # -----------------------------------------------------------------------------

    def disable_client(self, client_id: Optional[uuid.UUID] = None) -> bool:
        """Disable one or more clients"""
        result = False

        for client in self.__clients:
            if client_id is None or client_id.__eq__(client.id):
                result = client.disable()

        return result

    # -----------------------------------------------------------------------------

    def start(self) -> None:
        """Start clients loop"""
        for client in self.__clients:
            client.start()

    # -----------------------------------------------------------------------------

    def stop(self) -> None:
        """Stop clients loop"""
        for client in self.__clients:
            client.stop()

    # -----------------------------------------------------------------------------

    def is_connected(self) -> None:
        """Check if clients are connected"""
        for client in self.__clients:
            client.is_connected()

    # -----------------------------------------------------------------------------

    def loop(self) -> None:
        """Handle clients loop actions"""
        for client in self.__clients:
            client.handle()

    # -----------------------------------------------------------------------------

    def write_sensor(self, sensor_record: SensorRecord) -> None:
        """Write value to device sensor"""
        for client in self.__clients:
            client.write_sensor(sensor_record=sensor_record)


class ClientFactory:  # pylint: disable=too-few-public-methods
    """
    Plugin client factory

    @package        FastyBird:ShellyConnectorPlugin!
    @module         clients

    @author         Adam Kadlec <adam.kadlec@fastybird.com>
    """

    __client: Client

    __receiver: Receiver

    __devices_registry: DevicesRegistry
    __attributes_registry: AttributesRegistry
    __commands_registry: CommandsRegistry
    __blocks_registry: BlocksRegistry

    __logger: Logger

    # -----------------------------------------------------------------------------

    def __init__(  # pylint: disable=too-many-arguments
        self,
        client: Client,
        receiver: Receiver,
        devices_registry: DevicesRegistry,
        attributes_registry: AttributesRegistry,
        commands_registry: CommandsRegistry,
        blocks_registry: BlocksRegistry,
        logger: Logger,
    ) -> None:
        self.__client = client

        self.__receiver = receiver

        self.__devices_registry = devices_registry
        self.__attributes_registry = attributes_registry
        self.__commands_registry = commands_registry
        self.__blocks_registry = blocks_registry

        self.__logger = logger

    # -----------------------------------------------------------------------------

    def create(
        self,
        client_id: uuid.UUID,
        client_type: ClientType,
    ) -> None:
        """Create new client"""
        if client_type == ClientType.COAP:
            self.__client.register_client(
                client=CoapClient(
                    client_id=client_id,
                    client_state=True,
                    receiver=self.__receiver,
                    logger=self.__logger,
                ),
            )

            self.__logger.debug(
                "Created CoAP client: %s",
                client_id.__str__(),
                extra={
                    "client": {
                        "type": ClientType.COAP.value,
                        "id": client_id.__str__(),
                    },
                },
            )

            return

        if client_type == ClientType.MDNS:
            self.__client.register_client(
                client=MdnsClient(
                    client_id=client_id,
                    client_state=True,
                    receiver=self.__receiver,
                    logger=self.__logger,
                ),
            )

            self.__logger.debug(
                "Created mDNS client: %s",
                client_id.__str__(),
                extra={
                    "client": {
                        "type": ClientType.MDNS.value,
                        "id": client_id.__str__(),
                    },
                },
            )

            return

        if client_type == ClientType.HTTP:
            self.__client.register_client(
                client=HttpClient(
                    client_id=client_id,
                    client_state=True,
                    receiver=self.__receiver,
                    devices_registry=self.__devices_registry,
                    attributes_registry=self.__attributes_registry,
                    commands_registry=self.__commands_registry,
                    blocks_registry=self.__blocks_registry,
                    logger=self.__logger,
                ),
            )

            self.__logger.debug(
                "Created HTTP client: %s",
                client_id.__str__(),
                extra={
                    "client": {
                        "type": ClientType.HTTP.value,
                        "id": client_id.__str__(),
                    },
                },
            )

            return

        raise InvalidArgumentException(f"Unsupported client type: {client_type}")
