"""Configuration for dynamic_rest."""
import inspect

from django.conf import settings as django_settings
from django.test.signals import setting_changed

DYNAMIC_REST = {
    # DEBUG: enable/disable internal debugging
    "DEBUG": False,
    # ENABLE_BROWSABLE_API: enable/disable the browsable API.
    # It can be useful to disable it in production.
    "ENABLE_BROWSABLE_API": True,
    # ENABLE_LINKS: enable/disable relationship links
    "ENABLE_LINKS": True,
    # ENABLE_SERIALIZER_CACHE: enable/disable caching of related serializers
    "ENABLE_SERIALIZER_CACHE": True,
    # ENABLE_SERIALIZER_OBJECT_CACHE: enable/disable caching of serialized
    # objects within a serializer instance/context. This can yield
    # significant performance improvements in cases where the same objects
    # are sideloaded repeatedly.
    "ENABLE_SERIALIZER_OBJECT_CACHE": True,
    # ENABLE_SERIALIZER_OPTIMIZATIONS: enable/disable representation speedups
    "ENABLE_SERIALIZER_OPTIMIZATIONS": True,
    # ENABLE_BULK_PARTIAL_CREATION: enable/disable partial creation in bulk
    "ENABLE_BULK_PARTIAL_CREATION": False,
    # ENABLE_BULK_UPDATE: enable/disable update in bulk
    "ENABLE_BULK_UPDATE": True,
    # ENABLE_PATCH_ALL: enable/disable patch by queryset
    "ENABLE_PATCH_ALL": False,
    # DEFER_MANY_RELATIONS: automatically defer many-relations, unless
    # `deferred=False` is explicitly set on the field.
    "DEFER_MANY_RELATIONS": False,
    # LIST_SERIALIZER_CLASS: Globally override the list serializer class.
    # Default is `DynamicListSerializer` and also can be overridden for
    # each serializer class by setting `Meta.list_serializer_class`.
    "LIST_SERIALIZER_CLASS": None,
    # MAX_PAGE_SIZE: global setting for max page size.
    # Can be overriden at the viewset level.
    "MAX_PAGE_SIZE": None,
    # PAGE_QUERY_PARAM: global setting for the pagination query parameter.
    # Can be overriden at the viewset level.
    "PAGE_QUERY_PARAM": "page",
    # PAGE_SIZE: global setting for page size.
    # Can be overriden at the viewset level.
    "PAGE_SIZE": None,
    # PAGE_SIZE_QUERY_PARAM: global setting for the page size query parameter.
    # Can be overriden at the viewset level.
    "PAGE_SIZE_QUERY_PARAM": "per_page",
    # EXCLUDE_COUNT_QUERY_PARAM: global setting for the query parameter
    # that disables counting during PageNumber pagination
    "EXCLUDE_COUNT_QUERY_PARAM": "exclude_count",
    # ADDITIONAL_PRIMARY_RESOURCE_PREFIX: String to prefix additional
    # instances of the primary resource when sideloading.
    "ADDITIONAL_PRIMARY_RESOURCE_PREFIX": "+",
    # Enables host-relative links.  Only compatible with resources registered
    # through the dynamic router.  If a resource doesn't have a canonical
    # path registered, links will default back to being resource-relative urls
    "ENABLE_HOST_RELATIVE_LINKS": True,
    # Enables caching of serializer fields to speed up serializer usage
    # Needs to also be configured on a per-serializer basis
    "ENABLE_FIELDS_CACHE": False,
    # Enables use of hashid fields
    "ENABLE_HASHID_FIELDS": False,
    # Salt value to salt hash ids.
    # Needs to be non-nullable if 'ENABLE_HASHID_FIELDS' is set to True
    "HASHIDS_SALT": None,
}


# Attributes where the value should be a class (or path to a class)
CLASS_ATTRS = [
    "LIST_SERIALIZER_CLASS",
]


class Settings(object):
    """Settings class for dynamic rest."""

    def __init__(self, name, defaults, existing_settings, class_attrs=None):
        """Initialise the settings."""
        self.name = name
        self.defaults = defaults
        self.keys = set(defaults.keys())
        self.class_attrs = class_attrs

        self._cache = {}
        self._reload(getattr(existing_settings, self.name, {}))

        setting_changed.connect(self._settings_changed)

    def _reload(self, value):
        """Reload settings after a change."""
        self.settings = value
        self._cache = {}

    def _load_class(self, attr, val):
        """Load a class from a string."""
        if inspect.isclass(val):
            return val
        elif isinstance(val, str):
            parts = val.split(".")
            module_path = ".".join(parts[:-1])
            class_name = parts[-1]
            mod = __import__(module_path, fromlist=[class_name])
            return getattr(mod, class_name)
        elif val:
            raise ValueError(f"{attr} must be string or a class")

    def __getattr__(self, attr):
        """Get a setting."""
        if attr not in self._cache:
            if attr not in self.keys:
                raise AttributeError(f"Invalid API setting: '{attr}'")

            if attr in self.settings:
                val = self.settings[attr]
            else:
                val = self.defaults[attr]

            if attr in self.class_attrs and val:
                val = self._load_class(attr, val)

            # Cache the result
            self._cache[attr] = val

        return self._cache[attr]

    def _settings_changed(self, *_, **kwargs):
        """Handle changes to core settings."""
        setting, value = kwargs["setting"], kwargs["value"]
        if setting == self.name:
            self._reload(value)


settings = Settings("DYNAMIC_REST", DYNAMIC_REST, django_settings, CLASS_ATTRS)
