import sys
from collections import OrderedDict
from operator import attrgetter
from typing import Any, Dict, List, Set

from django.apps import AppConfig
from django.conf import settings
from django.db.models.signals import post_migrate


class CoreAppConfig(AppConfig):
    name = "openslides.core"
    verbose_name = "OpenSlides Core"
    angular_site_module = True

    def ready(self):
        # Import all required stuff.
        from .config import config
        from .projector import register_projector_elements
        from . import serializers  # noqa
        from .signals import (
            delete_django_app_permissions,
            get_permission_change_data,
            permission_change,
            post_permission_creation,
        )
        from .views import (
            ChatMessageViewSet,
            ConfigViewSet,
            CountdownViewSet,
            HistoryViewSet,
            ProjectorMessageViewSet,
            ProjectorViewSet,
            TagViewSet,
        )
        from .websocket import (
            NotifyWebsocketClientMessage,
            ConstantsWebsocketClientMessage,
            GetElementsWebsocketClientMessage,
            AutoupdateWebsocketClientMessage,
            ListenToProjectors,
        )
        from ..utils.access_permissions import required_user
        from ..utils.cache import element_cache
        from ..utils.constants import set_constants, get_constants_from_apps
        from ..utils.rest_api import router
        from ..utils.websocket import register_client_message

        # Collect all config variables before getting the constants.
        config.collect_config_variables_from_apps()

        # Skip all database related accesses during migrations.
        is_normal_server_start = False
        for sys_part in sys.argv:
            for entry in ("runserver", "gunicorn", "daphne", "create-example-data"):
                if sys_part.endswith(entry):
                    is_normal_server_start = True
                    break

        # Set constants
        if is_normal_server_start:
            set_constants(get_constants_from_apps())

        # Define projector elements.
        register_projector_elements()

        # Connect signals.
        post_permission_creation.connect(
            delete_django_app_permissions, dispatch_uid="delete_django_app_permissions"
        )
        permission_change.connect(
            get_permission_change_data, dispatch_uid="core_get_permission_change_data"
        )

        post_migrate.connect(
            call_save_default_values,
            sender=self,
            dispatch_uid="core_save_config_default_values",
        )

        # Register viewsets.
        router.register(
            self.get_model("Projector").get_collection_string(), ProjectorViewSet
        )
        router.register(
            self.get_model("ChatMessage").get_collection_string(), ChatMessageViewSet
        )
        router.register(self.get_model("Tag").get_collection_string(), TagViewSet)
        router.register(
            self.get_model("ConfigStore").get_collection_string(),
            ConfigViewSet,
            "config",
        )
        router.register(
            self.get_model("ProjectorMessage").get_collection_string(),
            ProjectorMessageViewSet,
        )
        router.register(
            self.get_model("Countdown").get_collection_string(), CountdownViewSet
        )
        router.register(
            self.get_model("History").get_collection_string(), HistoryViewSet
        )

        # Sets the cache and builds the startup history
        if is_normal_server_start:
            element_cache.ensure_cache()
            self.get_model("History").objects.build_history()

        # Register client messages
        register_client_message(NotifyWebsocketClientMessage())
        register_client_message(ConstantsWebsocketClientMessage())
        register_client_message(GetElementsWebsocketClientMessage())
        register_client_message(AutoupdateWebsocketClientMessage())
        register_client_message(ListenToProjectors())

        # register required_users
        required_user.add_collection_string(
            self.get_model("ChatMessage").get_collection_string(), required_users
        )

    def get_config_variables(self):
        from .config_variables import get_config_variables

        return get_config_variables()

    def get_startup_elements(self):
        """
        Yields all Cachables required on startup i. e. opening the websocket
        connection.
        """
        for model_name in (
            "Projector",
            "ChatMessage",
            "Tag",
            "ProjectorMessage",
            "Countdown",
            "ConfigStore",
            "History",
        ):