"""
eaigins_sdk.plugins
───────────────────
Plugin architecture for the Eaigins SDK.

Answers question 7 (Extensibility) — third parties can build and register:
    • Custom risk engines
    • Custom policy evaluators
    • Custom exporters (SIEM, OTEL, Datadog, etc.)
    • Custom firewall threat detectors
    • Compliance modules (HIPAA, SOC2, ISO27001 packs)

The plugin registry is a singleton per process.

Writing a plugin::

    from eaigins_sdk.plugins import (
        EaignsPlugin, PluginType,
        register_plugin, get_plugin,
    )
    from eaigins_sdk.risk import RiskEngine
    from eaigins_sdk.models import AgentAction, FirewallResult, PolicyResult, RiskScore

    class MyCustomRiskEngine(RiskEngine):
        def score(self, action, fw, pol) -> RiskScore:
            # Your custom scoring logic
            base = super().score(action, fw, pol)
            # boost regulatory score for healthcare
            if 'patient_ehr' in action.context_sources:
                boost = min(base.regulatory_exposure_score + 0.20, 1.0)
                # rebuild with boosted score
                ...
            return base

    plugin = EaignsPlugin(
        name         = "healthcare-risk-boost",
        version      = "1.0.0",
        plugin_type  = PluginType.RISK_ENGINE,
        description  = "Boosts regulatory exposure scores for PHI data sources.",
        factory      = lambda: MyCustomRiskEngine(),
        author       = "Acme Corp",
    )
    register_plugin(plugin)

    # Use it
    engine = get_plugin(PluginType.RISK_ENGINE, "healthcare-risk-boost")
    cp = ControlPlane(risk_engine=engine(), policies=HEALTHCARE_POLICIES)

Built-in plugin types::

    PluginType.RISK_ENGINE     — custom RiskEngine subclass
    PluginType.FIREWALL        — custom AIAgentFirewall subclass
    PluginType.EXPORTER        — audit trail / metrics exporter
    PluginType.POLICY_PACK     — pre-built policy collections
    PluginType.THREAT_DETECTOR — additional firewall threat patterns
    PluginType.COMPLIANCE_MODULE — domain-specific compliance packs
"""

from __future__ import annotations

import importlib
import json
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Callable, Optional


class PluginType(str, Enum):
    RISK_ENGINE        = "risk_engine"
    FIREWALL           = "firewall"
    EXPORTER           = "exporter"
    POLICY_PACK        = "policy_pack"
    THREAT_DETECTOR    = "threat_detector"
    COMPLIANCE_MODULE  = "compliance_module"


@dataclass
class EaignsPlugin:
    """Descriptor for a registered plugin."""
    name:        str
    version:     str
    plugin_type: PluginType
    description: str
    factory:     Callable[[], Any]    # callable that returns the plugin instance
    author:      str  = "unknown"
    homepage:    str  = ""
    metadata:    dict = field(default_factory=dict)

    def create(self) -> Any:
        """Instantiate the plugin via its factory."""
        return self.factory()

    def to_dict(self) -> dict:
        return {
            "name":        self.name,
            "version":     self.version,
            "type":        self.plugin_type.value,
            "description": self.description,
            "author":      self.author,
            "homepage":    self.homepage,
        }


class PluginRegistry:
    """Singleton plugin registry — one per process."""

    def __init__(self):
        self._plugins: dict[PluginType, dict[str, EaignsPlugin]] = {
            t: {} for t in PluginType
        }

    def register(self, plugin: EaignsPlugin) -> "PluginRegistry":
        """Register a plugin. Returns self for chaining."""
        self._plugins[plugin.plugin_type][plugin.name] = plugin
        return self

    def get(self, plugin_type: PluginType, name: str) -> Optional[EaignsPlugin]:
        """Retrieve a registered plugin by type and name."""
        return self._plugins.get(plugin_type, {}).get(name)

    def get_or_raise(self, plugin_type: PluginType, name: str) -> EaignsPlugin:
        plugin = self.get(plugin_type, name)
        if plugin is None:
            available = list(self._plugins.get(plugin_type, {}).keys())
            raise KeyError(
                f"Plugin {name!r} (type={plugin_type.value!r}) not found. "
                f"Registered: {available}"
            )
        return plugin

    def list_plugins(self, plugin_type: Optional[PluginType] = None) -> list[dict]:
        """List all registered plugins, optionally filtered by type."""
        results = []
        for ptype, plugins in self._plugins.items():
            if plugin_type is not None and ptype != plugin_type:
                continue
            for plugin in plugins.values():
                results.append(plugin.to_dict())
        return results

    def load_from_module(self, module_path: str) -> int:
        """
        Load plugins from a Python module by import path.
        The module must expose ``EAIGINS_PLUGINS: list[EaignsPlugin]``.

        Example::

            # my_company/eaigins_plugins.py
            from eaigins_sdk.plugins import EaignsPlugin, PluginType
            EAIGINS_PLUGINS = [
                EaignsPlugin(name="soc2-policies", ...),
                EaignsPlugin(name="custom-risk-engine", ...),
            ]

        Usage::

            registry.load_from_module("my_company.eaigins_plugins")
        """
        mod = importlib.import_module(module_path)
        plugins = getattr(mod, "EAIGINS_PLUGINS", [])
        for p in plugins:
            self.register(p)
        return len(plugins)

    def __len__(self) -> int:
        return sum(len(v) for v in self._plugins.values())

    def __repr__(self) -> str:
        counts = {t.value: len(v) for t, v in self._plugins.items() if v}
        return f"PluginRegistry({counts})"


# ── Singleton ──────────────────────────────────────────────────────────────────
_registry = PluginRegistry()


def register_plugin(plugin: EaignsPlugin) -> None:
    """Register a plugin in the global registry."""
    _registry.register(plugin)


def get_plugin(plugin_type: PluginType, name: str) -> EaignsPlugin:
    """Retrieve a plugin from the global registry."""
    return _registry.get_or_raise(plugin_type, name)


def list_plugins(plugin_type: Optional[PluginType] = None) -> list[dict]:
    """List all registered plugins."""
    return _registry.list_plugins(plugin_type)


def get_registry() -> PluginRegistry:
    """Access the global plugin registry directly."""
    return _registry


# ── Built-in compliance module plugin helpers ─────────────────────────────────

def register_policy_pack(
    name:     str,
    policies: list,
    version:  str  = "1.0.0",
    author:   str  = "community",
    description: str = "",
) -> EaignsPlugin:
    """
    Convenience function to register a policy pack plugin.

    Example::

        from eaigins_sdk.plugins import register_policy_pack
        from eaigins_sdk.policies import HEALTHCARE_POLICIES

        register_policy_pack(
            name        = "hipaa-extended",
            policies    = HEALTHCARE_POLICIES + MY_EXTRA_POLICIES,
            version     = "2.1.0",
            author      = "Acme Healthcare",
            description = "Extended HIPAA policy pack with additional PHI controls.",
        )

        # Later, use the registered pack
        plugin = get_plugin(PluginType.POLICY_PACK, "hipaa-extended")
        policies = plugin.create()
        cp = ControlPlane(policies=policies)
    """
    _policies = list(policies)
    plugin = EaignsPlugin(
        name        = name,
        version     = version,
        plugin_type = PluginType.POLICY_PACK,
        description = description or f"Policy pack: {name}",
        factory     = lambda p=_policies: p,
        author      = author,
    )
    _registry.register(plugin)
    return plugin
