"""Plugin discovery via entry points and settings config.

Scans ``pycharter.extractors`` and ``pycharter.loaders`` entry-point groups
and registers discovered classes with :class:`ExtractorFactory` /
:class:`LoaderFactory`.  Called lazily on the first ``Factory.create()``
invocation so import-time side effects are avoided.
"""

from __future__ import annotations

import importlib
import logging
from typing import Any

logger = logging.getLogger(__name__)

_discovered = False


def discover_plugins() -> None:
    """Scan entry points and register extractors/loaders.

    Uses ``importlib.metadata.entry_points()`` to find plugins declared under
    the ``pycharter.extractors`` and ``pycharter.loaders`` groups.  Each entry
    point maps *name -> module.path:ClassName*.

    This function is idempotent — subsequent calls are no-ops.
    """
    global _discovered  # noqa: PLW0603
    if _discovered:
        return
    _discovered = True

    _discover_entry_points()


def _discover_entry_points() -> None:
    """Load entry-point plugins and register them with factories."""
    from importlib.metadata import entry_points

    from pycharter.etl_generator.extractors.base import BaseExtractor
    from pycharter.etl_generator.extractors.factory import ExtractorFactory
    from pycharter.etl_generator.loaders.base import BaseLoader
    from pycharter.etl_generator.loaders.factory import LoaderFactory

    _register_group(
        entry_points(group="pycharter.extractors"),
        ExtractorFactory,
        BaseExtractor,
        "extractor",
    )
    _register_group(
        entry_points(group="pycharter.loaders"),
        LoaderFactory,
        BaseLoader,
        "loader",
    )


def _register_group(eps, factory, base_class, kind: str) -> None:
    """Register all entry points in a group with the appropriate factory."""
    for ep in eps:
        try:
            cls = ep.load()
        except Exception:
            logger.warning("Failed to load %s plugin %r", kind, ep.name, exc_info=True)
            continue

        if not issubclass(cls, base_class):
            logger.warning(
                "Plugin %r (%s) is not a subclass of %s — skipped",
                ep.name,
                cls,
                base_class.__name__,
            )
            continue

        factory.register(ep.name, cls)
        logger.debug("Registered %s plugin: %s -> %s", kind, ep.name, cls.__name__)


def load_plugins_from_settings(settings: dict[str, Any]) -> None:
    """Register plugins declared in the ``plugins`` section of settings.

    Args:
        settings: Parsed settings dict (may contain a ``plugins`` key).

    The ``plugins`` section has the shape::

        plugins:
          extractors:
            kafka: "mypackage.extractors:KafkaExtractor"
          loaders:
            bigquery: "mypackage.loaders:BigQueryLoader"
    """
    plugins = settings.get("plugins")
    if not plugins:
        return

    from pycharter.etl_generator.extractors.base import BaseExtractor
    from pycharter.etl_generator.extractors.factory import ExtractorFactory
    from pycharter.etl_generator.loaders.base import BaseLoader
    from pycharter.etl_generator.loaders.factory import LoaderFactory

    for name, ref in (plugins.get("extractors") or {}).items():
        _import_and_register(name, ref, ExtractorFactory, BaseExtractor, "extractor")

    for name, ref in (plugins.get("loaders") or {}).items():
        _import_and_register(name, ref, LoaderFactory, BaseLoader, "loader")


def _import_and_register(name: str, ref: str, factory, base_class, kind: str) -> None:
    """Import *ref* (``module.path:Class``) and register with *factory*."""
    try:
        module_path, class_name = ref.rsplit(":", 1)
        module = importlib.import_module(module_path)
        cls = getattr(module, class_name)
    except Exception:
        logger.warning(
            "Failed to import %s plugin %r from %r", kind, name, ref, exc_info=True
        )
        return

    if not issubclass(cls, base_class):
        logger.warning(
            "Plugin %r (%s) is not a subclass of %s — skipped",
            name,
            cls,
            base_class.__name__,
        )
        return

    factory.register(name, cls)
    logger.debug(
        "Registered %s plugin from settings: %s -> %s", kind, name, cls.__name__
    )


def reset_discovery() -> None:
    """Reset discovery state (for testing only)."""
    global _discovered  # noqa: PLW0603
    _discovered = False
