"""Discovery and handling of bundled Claude plugins from other Pants backends.

Pants plugins can declare Claude Code plugins that should be installed alongside
them by defining a `BUNDLED_CLAUDE_PLUGINS` list in a `bundled_claude_plugins` module.

Example in another Pants plugin:

    # src/my_plugin/bundled_claude_plugins.py

    # Marketplaces to add before installing plugins (GitHub repos or URLs)
    BUNDLED_MARKETPLACES = [
        "anthropics/claude-code",  # GitHub owner/repo format
        "https://gitlab.com/company/plugins.git",  # Full git URL
    ]

    BUNDLED_CLAUDE_PLUGINS = [
        {
            "plugin": "github",
            "marketplace": "claude-plugins-official",
            "scope": "project",  # optional, defaults to subsystem default
            "description": "GitHub integration",  # optional
        },
        {
            "plugin": "commit-commands",
            "marketplace": "anthropics-claude-code",  # This marketplace is auto-added above
        },
    ]

Then register it in your plugin's register.py or __init__.py by adding
the module to be discoverable.
"""

from __future__ import annotations

import importlib
import logging
from dataclasses import dataclass
from typing import Any

from pants.engine.rules import collect_rules, rule
from pants.engine.unions import UnionRule

from pants_claude_plugins.rules import ClaudePluginInstallRequest

logger = logging.getLogger(__name__)


# Well-known module name for bundled plugins
BUNDLED_MODULE_NAME = "bundled_claude_plugins"
BUNDLED_VAR_NAME = "BUNDLED_CLAUDE_PLUGINS"
BUNDLED_MARKETPLACES_VAR_NAME = "BUNDLED_MARKETPLACES"


@dataclass(frozen=True)
class BundledPluginSource:
    """A source of bundled Claude plugins (i.e., a Pants backend package)."""

    package_name: str


@dataclass(frozen=True)
class DiscoverBundledPluginsRequest:
    """Request to discover bundled plugins from all loaded backends."""

    backend_packages: tuple[str, ...]


@dataclass(frozen=True)
class DiscoveredBundledPlugins:
    """Result of discovering bundled plugins."""

    plugins: tuple[ClaudePluginInstallRequest, ...]
    marketplaces: tuple[str, ...]  # Marketplaces to add before installing
    sources: tuple[str, ...]  # Which packages provided plugins


def discover_bundled_plugins_from_package(
    package_name: str,
    default_scope: str,
) -> tuple[list[ClaudePluginInstallRequest], list[str]]:
    """Attempt to discover bundled Claude plugins from a package.

    Args:
        package_name: The Python package name to check
        default_scope: Default scope to use if not specified in plugin definition

    Returns:
        Tuple of (install requests, marketplaces to add)
    """
    # Try to import the bundled_claude_plugins module from this package
    module_name = f"{package_name}.{BUNDLED_MODULE_NAME}"

    try:
        module = importlib.import_module(module_name)
    except ImportError:
        # No bundled plugins module - that's fine
        return [], []

    # Get marketplaces to add
    marketplaces_list = getattr(module, BUNDLED_MARKETPLACES_VAR_NAME, [])
    marketplaces = []
    if isinstance(marketplaces_list, list):
        for item in marketplaces_list:
            if isinstance(item, str):
                marketplaces.append(item)
            else:
                logger.warning(f"Invalid marketplace entry in {module_name}: {item}")

    # Get plugins to install
    bundled_list = getattr(module, BUNDLED_VAR_NAME, None)
    if not bundled_list or not isinstance(bundled_list, list):
        logger.debug(f"No {BUNDLED_VAR_NAME} found in {module_name}")
        return [], marketplaces

    plugins = []
    for item in bundled_list:
        if not isinstance(item, dict):
            logger.warning(f"Invalid bundled plugin entry in {module_name}: {item}")
            continue

        plugin_name = item.get("plugin")
        marketplace = item.get("marketplace")

        if not plugin_name or not marketplace:
            logger.warning(
                f"Bundled plugin in {module_name} missing required fields: {item}"
            )
            continue

        plugins.append(
            ClaudePluginInstallRequest(
                plugin_name=plugin_name,
                marketplace=marketplace,
                scope=item.get("scope", default_scope),
            )
        )

    if plugins:
        logger.info(f"Discovered {len(plugins)} bundled plugin(s) from {package_name}")
    if marketplaces:
        logger.info(f"Discovered {len(marketplaces)} marketplace(s) to add from {package_name}")

    return plugins, marketplaces


def discover_all_bundled_plugins(
    backend_packages: tuple[str, ...],
    default_scope: str,
) -> DiscoveredBundledPlugins:
    """Discover bundled plugins from all provided backend packages.

    Args:
        backend_packages: Tuple of backend package names to check
        default_scope: Default installation scope

    Returns:
        DiscoveredBundledPlugins with all found plugins, marketplaces, and sources
    """
    all_plugins: list[ClaudePluginInstallRequest] = []
    all_marketplaces: list[str] = []
    sources: list[str] = []

    for package in backend_packages:
        plugins, marketplaces = discover_bundled_plugins_from_package(package, default_scope)
        if plugins or marketplaces:
            all_plugins.extend(plugins)
            all_marketplaces.extend(marketplaces)
            sources.append(package)

    # Deduplicate marketplaces while preserving order
    seen_marketplaces: set[str] = set()
    unique_marketplaces: list[str] = []
    for m in all_marketplaces:
        if m not in seen_marketplaces:
            seen_marketplaces.add(m)
            unique_marketplaces.append(m)

    return DiscoveredBundledPlugins(
        plugins=tuple(all_plugins),
        marketplaces=tuple(unique_marketplaces),
        sources=tuple(sources),
    )


def rules():
    """Return rules for bundled plugin discovery."""
    return collect_rules()
