text: #!/usr/bin/python3
  
  #     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.
  
  """
  FastyBird gateway DI container
  """
  
  # pylint: disable=no-value-for-parameter
  
  # Python base dependencies
  import logging
  import logging.config
  import logging.handlers
  from os import path
  
  # App dependencies
  from devices_module.bootstrap import create_container as initialize_devices_module
  from exchange_plugin.bootstrap import create_container as initialize_exchange_plugin
  from fb_bus_connector_plugin.bootstrap import (
      create_container as initialize_fb_bus_connector_plugin,
  )
  from fb_bus_connector_plugin.consumers.consumer import Consumer as FbBusMessagesConsumer
  from fb_mqtt_connector_plugin.bootstrap import (
      create_container as initialize_fb_mqtt_connector_plugin,
  )
  from fb_mqtt_connector_plugin.consumers.consumer import (
      Consumer as FbMqttMessagesConsumer,
  )
  from kink import di
  from redisdb_exchange_plugin.bootstrap import (
      create_container as initialize_redisdb_exchange_plugin,
  )
  from redisdb_exchange_plugin.exchange import RedisExchange
  from triggers_module.bootstrap import create_container as initialize_triggers_module
  from ws_server_plugin.bootstrap import create_container as initialize_ws_server_plugin
  from ws_server_plugin.server import WebsocketsServer
  from yaml import safe_load
  
  # App libs
  from miniserver_gateway.connectors.connector import Connector
  from miniserver_gateway.connectors.fb_bus.connector import FbBusConnectorWrapper
  from miniserver_gateway.connectors.fb_bus.consumers.device import DeviceItemConsumer
  from miniserver_gateway.connectors.fb_bus.consumers.register import RegisterItemConsumer
  from miniserver_gateway.connectors.fb_mqtt.connector import FbMqttConnectorWrapper
  from miniserver_gateway.connectors.fb_mqtt.consumers.channel import (
      ChannelAttributeItemConsumer,
  )
  from miniserver_gateway.connectors.fb_mqtt.consumers.control import (
      ChannelControlItemConsumer,
      DeviceControlItemConsumer,
  )
  from miniserver_gateway.connectors.fb_mqtt.consumers.device import (
      DeviceAttributeItemConsumer,
      DeviceFirmwareItemConsumer,
      DeviceHardwareItemConsumer,
  )
  from miniserver_gateway.connectors.fb_mqtt.consumers.property import (
      ChannelPropertyItemConsumer,
      DevicePropertyItemConsumer,
  )
  from miniserver_gateway.connectors.modbus.bootstrap import (
      create_container as initialize_modbus_connector_plugin,
  )
  from miniserver_gateway.connectors.modbus.connector import ModbusConnectorWrapper
  from miniserver_gateway.connectors.queue import ConnectorsQueue
  from miniserver_gateway.connectors.shelly.bootstrap import (
      create_container as initialize_shelly_connector_plugin,
  )
  from miniserver_gateway.connectors.shelly.connector import ShellyConnectorWrapper
  from miniserver_gateway.connectors.shelly.consumers.channel import (
      ChannelItemConsumer as ShellyChannelItemConsumer,
  )
  from miniserver_gateway.connectors.shelly.consumers.consumer import (
      Consumer as ShellyMessagesConsumer,
  )
  from miniserver_gateway.connectors.shelly.consumers.device import (
      DeviceItemConsumer as ShellyDeviceItemConsumer,
  )
  from miniserver_gateway.connectors.shelly.consumers.property import (
      PropertyActualValueConsumer as ShellyPropertyActualValueConsumer,
  )
  from miniserver_gateway.connectors.shelly.consumers.property import (
      PropertyItemConsumer as ShellyPropertyItemConsumer,
  )
  from miniserver_gateway.connectors.subscriber import ConnectorsSubscriber
  from miniserver_gateway.db.db import Database
  from miniserver_gateway.db.devices.db import DevicesDatabase
  from miniserver_gateway.db.queue import DatabaseQueue
  from miniserver_gateway.db.subscriber import DatabaseSubscriber
  from miniserver_gateway.db.triggers.db import TriggersDatabase
  from miniserver_gateway.exchanges.consumer import ExchangesConsumer
  from miniserver_gateway.exchanges.exchange import Exchange
  from miniserver_gateway.exchanges.queue import ExchangeQueue
  from miniserver_gateway.exchanges.subscriber import ExchangeSubscriber
  from miniserver_gateway.gateway.fb_gateway_service import FBGatewayService
  from miniserver_gateway.homekit.homekit import HomeKit
  from miniserver_gateway.storages.devices.repository import DevicesRepository
  from miniserver_gateway.storages.devices.storage import DevicesStorage
  from miniserver_gateway.storages.queue import StorageQueue
  from miniserver_gateway.storages.storage import Storage
  from miniserver_gateway.storages.subscriber import StorageSubscriber
  from miniserver_gateway.storages.triggers.storage import TriggersStorage
  
  
  def create_container(config_file: str) -> FBGatewayService:  # pylint: disable=too-many-statements
      """Create APP services"""
      with open(file=config_file, encoding="utf-8") as general_config:
          configuration = safe_load(general_config)
  
      configuration_dir = path.dirname(path.abspath(config_file)) + path.sep
  
      app_logger = logging.getLogger("service")
  
      try:
          logging.config.fileConfig(configuration_dir + "logs.conf", disable_existing_loggers=False)
  
      except Exception as ex:  # pylint: disable=broad-except
          app_logger.error("Configuration file could not be loaded")
          app_logger.exception(ex)
  
          raise Exception("Configuration file could not be loaded") from ex
  
      # Initialize communication plugin
      initialize_exchange_plugin()
  
      # Initialize modules
      initialize_devices_module(
          settings={
              "database": {
                  "host": configuration.get("database").get("host"),
                  "username": configuration.get("database").get("user"),
                  "password": configuration.get("database").get("passwd"),
                  "database": configuration.get("database").get("db"),
              },
          }
      )
      initialize_triggers_module(
          settings={
              "database": {
                  "host": configuration.get("database").get("host"),
                  "username": configuration.get("database").get("user"),
                  "password": configuration.get("database").get("passwd"),
                  "database": configuration.get("database").get("db"),
              },
          }
      )
  
      # Initialize plugins
      initialize_ws_server_plugin(logger=logging.getLogger("exchange"))
      initialize_redisdb_exchange_plugin(settings=configuration.get("redis"), logger=logging.getLogger("exchange"))
      initialize_fb_mqtt_connector_plugin(logger=logging.getLogger("connectors"))
      initialize_fb_bus_connector_plugin(logger=logging.getLogger("connectors"))
      initialize_shelly_connector_plugin(logger=logging.getLogger("connectors"))
      initialize_modbus_connector_plugin(logger=logging.getLogger("connectors"))
  
      # Database services
      database_logger = logging.getLogger("database")
  
      di[DevicesDatabase] = DevicesDatabase(  # type: ignore[call-arg]
          logger=database_logger,
      )
      di[TriggersDatabase] = TriggersDatabase(  # type: ignore[call-arg]
          logger=database_logger,
      )
      di[DatabaseQueue] = DatabaseQueue(
          devices_database=di[DevicesDatabase],
          triggers_database=di[TriggersDatabase],
          logger=database_logger,
      )
      di[DatabaseSubscriber] = DatabaseSubscriber(  # type: ignore[call-arg]
          database_queue=di[DatabaseQueue],
          logger=database_logger,
      )
      di[Database] = Database(
          database_queue=di[DatabaseQueue],
          database_subscriber=di[DatabaseSubscriber],
          logger=database_logger,
      )
  
      # Exchange service
      exchange_logger = logging.getLogger("exchange")
  
      di[ExchangeQueue] = ExchangeQueue(  # type: ignore[call-arg]
          logger=exchange_logger,
      )
      di[ExchangeSubscriber] = ExchangeSubscriber(  # type: ignore[call-arg]
          exchange_queue=di[ExchangeQueue],
          logger=exchange_logger,
      )
      di[ExchangesConsumer] = ExchangesConsumer(  # type: ignore[call-arg]
          logger=exchange_logger,
      )
      di[Exchange] = Exchange(  # type: ignore[call-arg]
          exchange_queue=di[ExchangeQueue],
          exchange_subscriber=di[ExchangeSubscriber],
          logger=exchange_logger,
      )
      di[WebsocketsServer].register_consumer(consumer=di[ExchangesConsumer])
      di[RedisExchange].register_consumer(consumer=di[ExchangesConsumer])
  
      # Storage service
      storage_logger = logging.getLogger("storage")
  
      di[DevicesStorage] = DevicesStorage(  # type: ignore[call-arg]
          configuration=configuration,
          logger=storage_logger,
      )
      di[DevicesRepository] = DevicesRepository(  # type: ignore[call-arg]
          configuration=configuration,
          logger=storage_logger,
      )
      di[TriggersStorage] = TriggersStorage(  # type: ignore[call-arg]
          configuration=configuration,
          logger=storage_logger,
      )
      di[StorageQueue] = StorageQueue(
          devices_storage=di[DevicesStorage],
          triggers_storage=di[TriggersStorage],
          logger=storage_logger,
      )
      di[StorageSubscriber] = StorageSubscriber(  # type: ignore[call-arg]
          storage_queue=di[StorageQueue],
          logger=storage_logger,
      )
      di[Storage] = Storage(
          storage_queue=di[StorageQueue],
          storage_subscriber=di[StorageSubscriber],
          devices_storage=di[DevicesStorage],
          devices_repository=di[DevicesRepository],
          triggers_storage=di[TriggersStorage],
          logger=storage_logger,
      )
  
      # Connectors services
      connectors_logger = logging.getLogger("connectors")
  
      # FB BUS connector receivers
      di[DeviceItemConsumer] = DeviceItemConsumer()  # type: ignore[call-arg]
      di[RegisterItemConsumer] = RegisterItemConsumer()  # type: ignore[call-arg]
  
      di[FbBusMessagesConsumer].register_consumer(consumer=di[DeviceItemConsumer])
      di[FbBusMessagesConsumer].register_consumer(consumer=di[RegisterItemConsumer])
  
      # MQTT connector receivers
      di[ChannelAttributeItemConsumer] = ChannelAttributeItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[DeviceControlItemConsumer] = DeviceControlItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[ChannelControlItemConsumer] = ChannelControlItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[DeviceAttributeItemConsumer] = DeviceAttributeItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[DeviceHardwareItemConsumer] = DeviceHardwareItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[DeviceFirmwareItemConsumer] = DeviceFirmwareItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[DevicePropertyItemConsumer] = DevicePropertyItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[ChannelPropertyItemConsumer] = ChannelPropertyItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
  
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[ChannelAttributeItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[DeviceControlItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[ChannelControlItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[DeviceAttributeItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[DeviceHardwareItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[DeviceFirmwareItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[DevicePropertyItemConsumer])
      di[FbMqttMessagesConsumer].register_consumer(consumer=di[ChannelPropertyItemConsumer])
  
      # Shelly connector receivers
      di[ShellyDeviceItemConsumer] = ShellyDeviceItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[ShellyChannelItemConsumer] = ShellyChannelItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[ShellyPropertyItemConsumer] = ShellyPropertyItemConsumer(logger=connectors_logger)  # type: ignore[call-arg]
      di[ShellyPropertyActualValueConsumer] = ShellyPropertyActualValueConsumer(  # type: ignore[call-arg]
          logger=connectors_logger
      )
  
      di[ShellyMessagesConsumer].register_consumer(consumer=di[ShellyDeviceItemConsumer])
      di[ShellyMessagesConsumer].register_consumer(consumer=di[ShellyChannelItemConsumer])
      di[ShellyMessagesConsumer].register_consumer(consumer=di[ShellyPropertyItemConsumer])
      di[ShellyMessagesConsumer].register_consumer(consumer=di[ShellyPropertyActualValueConsumer])
  
      di[FbBusConnectorWrapper] = FbBusConnectorWrapper(logger=connectors_logger)  # type: ignore[call-arg]
      di[FbMqttConnectorWrapper] = FbMqttConnectorWrapper(logger=connectors_logger)  # type: ignore[call-arg]
      di[ShellyConnectorWrapper] = ShellyConnectorWrapper(logger=connectors_logger)  # type: ignore[call-arg]
      di[ModbusConnectorWrapper] = ModbusConnectorWrapper(logger=connectors_logger)  # type: ignore[call-arg]
  
      di[ConnectorsQueue] = ConnectorsQueue(
          connectors=[
              di[FbBusConnectorWrapper],
              di[FbMqttConnectorWrapper],
              di[ShellyConnectorWrapper],
              di[ModbusConnectorWrapper],
          ],
          logger=connectors_logger,
      )
  
      di[ConnectorsSubscriber] = ConnectorsSubscriber(  # type: ignore[call-arg]
          connectors_queue=di[ConnectorsQueue],
          logger=connectors_logger,
      )
  
      di[Connector] = Connector(
          connectors=[
              di[FbBusConnectorWrapper],
              di[FbMqttConnectorWrapper],
              di[ShellyConnectorWrapper],
              di[ModbusConnectorWrapper],
          ],
          connectors_queue=di[ConnectorsQueue],
          connectors_subscriber=di[ConnectorsSubscriber],
          logger=connectors_logger,
      )
  
      di[HomeKit] = HomeKit()
  
      # Application
      di[FBGatewayService] = FBGatewayService(
          database=di[Database],
          exchange=di[Exchange],
          storage=di[Storage],
          connectors=di[Connector],
          home_kit=di[HomeKit],
          logger=app_logger,
      )
  
      return di[FBGatewayService]  # type: ignore[no-any-return]
  