from importlib import import_module

from dependency_injector import containers, providers

from isar.apis.api import API
from isar.apis.robot_control.robot_controller import RobotController
from isar.apis.schedule.scheduling_controller import SchedulingController
from isar.apis.security.authentication import Authenticator
from isar.config.settings import settings
from isar.models.events import Events, SharedState
from isar.robot.robot import Robot
from isar.services.utilities.robot_utilities import RobotUtilities
from isar.services.utilities.scheduling_utilities import SchedulingUtilities
from isar.state_machine.state_machine import StateMachine
from isar.storage.blob_storage import BlobStorage
from isar.storage.local_storage import LocalStorage
from isar.storage.uploader import Uploader
from robot_interface.telemetry.mqtt_client import MqttPublisher


class ApplicationContainer(containers.DeclarativeContainer):
    config = providers.Configuration(pydantic_settings=[settings])

    # Events and shared state
    events = providers.Singleton(Events)
    shared_state = providers.Singleton(SharedState)

    # Robot-related services
    robot_interface = providers.Singleton(
        lambda: import_module(f"{settings.ROBOT_PACKAGE}.robotinterface").Robot()
    )
    robot_utilities = providers.Singleton(RobotUtilities, robot=robot_interface)

    # Mqtt client
    mqtt_client = providers.Singleton(
        MqttPublisher,
        mqtt_queue=providers.Callable(events.provided.mqtt_queue),
    )

    # API and controllers
    authenticator = providers.Singleton(Authenticator)
    scheduling_utilities = providers.Singleton(
        SchedulingUtilities,
        events=events,
        shared_state=shared_state,
    )
    scheduling_controller = providers.Singleton(
        SchedulingController, scheduling_utilities=scheduling_utilities
    )
    robot_controller = providers.Singleton(
        RobotController, robot_utilities=robot_utilities
    )
    api = providers.Singleton(
        API,
        authenticator=authenticator,
        scheduling_controller=scheduling_controller,
        robot_controller=robot_controller,
        mqtt_publisher=mqtt_client,
    )

    # Storage
    storage_handlers_temp = []
    if settings.STORAGE_LOCAL_ENABLED:
        local_storage = providers.Singleton(LocalStorage)
        storage_handlers_temp.append(local_storage)
    if settings.STORAGE_BLOB_ENABLED:
        blob_storage = providers.Singleton(BlobStorage)
        storage_handlers_temp.append(blob_storage)
    storage_handlers = providers.List(*storage_handlers_temp)

    # State machine
    state_machine = providers.Singleton(
        StateMachine,
        events=events,
        shared_state=shared_state,
        robot=robot_interface,
        mqtt_publisher=mqtt_client,
    )

    # Robot
    robot = providers.Singleton(
        Robot,
        events=events,
        robot=robot_interface,
        shared_state=shared_state,
        mqtt_publisher=mqtt_client,
    )

    # Uploader
    uploader = providers.Singleton(
        Uploader,
        events=events,
        storage_handlers=storage_handlers,
        mqtt_publisher=mqtt_client,
    )


def get_injector() -> ApplicationContainer:
    container = ApplicationContainer()
    container.init_resources()
    container.wire(modules=[__name__])

    print("Loaded the following module configurations:")
    for provider_name, provider in container.providers.items():
        provider_repr = repr(provider)
        simplified_provider = provider_repr.split(".")[-1].split(">")[0]
        print(f"    {provider_name:<20}: {simplified_provider}")

    return container
