from typing import Any, ClassVar, Dict, Optional

from pydantic import Field
from pydantic_settings import BaseSettings

from powermem.settings import settings_config


class BaseEmbedderConfig(BaseSettings):
    """Common embedding configuration shared by all providers."""

    model_config = settings_config("EMBEDDING_", extra="allow", env_file=None)

    _provider_name: ClassVar[Optional[str]] = None
    _class_path: ClassVar[Optional[str]] = None
    _registry: ClassVar[dict[str, type["BaseEmbedderConfig"]]] = {}
    _class_paths: ClassVar[dict[str, str]] = {}

    @classmethod
    def _register_provider(cls) -> None:
        provider = getattr(cls, "_provider_name", None)
        class_path = getattr(cls, "_class_path", None)
        if provider:
            BaseEmbedderConfig._registry[provider] = cls
            if class_path:
                BaseEmbedderConfig._class_paths[provider] = class_path

    def __init_subclass__(cls, **kwargs) -> None:
        super().__init_subclass__(**kwargs)

    @classmethod
    def __pydantic_init_subclass__(cls, **kwargs) -> None:
        super().__pydantic_init_subclass__(**kwargs)
        cls._register_provider()

    @classmethod
    def get_provider_config_cls(cls, provider: str) -> Optional[type["BaseEmbedderConfig"]]:
        return cls._registry.get(provider)

    @classmethod
    def get_provider_class_path(cls, provider: str) -> Optional[str]:
        return cls._class_paths.get(provider)

    @classmethod
    def has_provider(cls, provider: str) -> bool:
        return provider in cls._registry

    model: Optional[Any] = Field(
        default=None,
        description="Embedding model name or provider-specific model object.",
    )
    api_key: Optional[str] = Field(
        default=None,
        description="API key used for provider authentication.",
    )
    embedding_dims: Optional[int] = Field(
        default=None,
        description="Embedding vector dimensions, when configurable by provider.",
    )

    @property
    def provider(self) -> Optional[str]:
        """Return the provider name."""
        # 1. Try the class-level _provider_name
        p = getattr(type(self), "_provider_name", None)
        if p:
            return p
        # 2. Try extra fields (e.g. if loaded from a dict that had a "provider" key)
        if hasattr(self, "model_extra") and self.model_extra:
            return self.model_extra.get("provider")
        return None

    @property
    def config(self) -> Dict[str, Any]:
        """Return the configuration dictionary."""
        # 1. Try extra fields (e.g. if loaded from a dict that had a "config" key)
        if hasattr(self, "model_extra") and self.model_extra and "config" in self.model_extra:
            val = self.model_extra["config"]
            if isinstance(val, dict):
                return val
        # 2. Return the model fields, excluding provider/config if they are extras
        exclude = {"provider", "config"}
        if hasattr(self, "model_dump"):
            return self.model_dump(exclude_none=True, exclude=exclude)
        return getattr(self, "dict", lambda **kwargs: {})(exclude_none=True, exclude=exclude)

    def to_component_dict(self) -> Dict[str, Any]:
        return {
            "provider": self.provider,
            "config": self.config
        }
