from django.apps import apps
from django.core.exceptions import ImproperlyConfigured

from .models import SyncModel
from .sync_meta import SyncMetaOptions, SyncScope


class djinoRegistry:
    """Discovers and registers all SyncModel subclasses."""

    _models: dict[str, type] = {}
    _meta: dict[str, SyncMetaOptions] = {}

    @classmethod
    def autodiscover(cls):
        cls._models = {}
        cls._meta = {}
        for model in apps.get_models():
            if issubclass(model, SyncModel) and not model._meta.abstract:
                key = f"{model._meta.app_label}.{model.__name__}"
                cls._models[key] = model
                cls._meta[key] = cls._parse_sync_meta(model)

    @classmethod
    def _parse_sync_meta(cls, model) -> SyncMetaOptions:
        meta_class = getattr(model, "SyncMeta", None)
        options = SyncMetaOptions()

        if meta_class is None:
            return options

        for attr in ("scope", "sync_filter", "client_writable", "write_check", "owner_field"):
            if hasattr(meta_class, attr):
                setattr(options, attr, getattr(meta_class, attr))

        cls._validate_sync_meta(model, options)
        return options

    @classmethod
    def _validate_sync_meta(cls, model, options: SyncMetaOptions):
        if options.scope == SyncScope.SHARED and options.sync_filter is None:
            raise ImproperlyConfigured(f"{model.__name__}: SHARED scope requires sync_filter")

        if options.scope == SyncScope.USER and options.owner_field is None:
            raise ImproperlyConfigured(f"{model.__name__}: USER scope requires owner_field")

        if options.owner_field:
            try:
                model._meta.get_field(options.owner_field)
            except Exception:
                raise ImproperlyConfigured(
                    f"{model.__name__}: owner_field '{options.owner_field}' "
                    f"does not exist on model"
                )

        if options.scope == SyncScope.GLOBAL and options.client_writable:
            raise ImproperlyConfigured(
                f"{model.__name__}: GLOBAL scope should not be client_writable. "
                f"Set client_writable = False."
            )

    @classmethod
    def get_models(cls) -> dict[str, type]:
        return cls._models.copy()

    @classmethod
    def get_meta(cls, model_key: str) -> SyncMetaOptions:
        return cls._meta[model_key]
