"""
Plugin system for extending MCP functionality
"""

import importlib
import inspect
import pkgutil
import sys
from abc import ABC, abstractmethod
from collections.abc import Callable
from pathlib import Path
from typing import Any, Dict, List, Optional, Type

from .logging import get_structured_logger


class PluginBase(ABC):
    """Base class for all plugins."""

    def __init__(self, name: str, version: str = "1.0.0"):
        self.name = name
        self.version = version
        self.logger = get_structured_logger()
        self._enabled = False

    @abstractmethod
    def initialize(self, config: dict[str, Any]) -> bool:
        """Initialize the plugin with configuration."""
        pass

    @abstractmethod
    def cleanup(self) -> None:
        """Cleanup plugin resources."""
        pass

    def enable(self) -> None:
        """Enable the plugin."""
        self._enabled = True
        self.logger.log_performance_metric("plugin_enabled", 1, {"plugin": self.name})

    def disable(self) -> None:
        """Disable the plugin."""
        self._enabled = False
        self.logger.log_performance_metric("plugin_disabled", 1, {"plugin": self.name})

    def is_enabled(self) -> bool:
        """Check if plugin is enabled."""
        return self._enabled


class ToolPlugin(PluginBase):
    """Plugin for adding new tools."""

    def __init__(self, name: str, version: str = "1.0.0"):
        super().__init__(name, version)
        self.tools: dict[str, Callable] = {}

    def add_tool(self, tool_name: str, tool_function: Callable) -> None:
        """Add a tool to the plugin."""
        self.tools[tool_name] = tool_function

    def get_tools(self) -> dict[str, Callable]:
        """Get all tools provided by this plugin."""
        return self.tools.copy()

    def initialize(self, config: dict[str, Any]) -> bool:
        """Initialize tool plugin."""
        try:
            # Plugin-specific initialization logic
            self.logger.log_performance_metric(
                "plugin_initialized", 1, {"plugin": self.name, "type": "tool"}
            )
            return True
        except Exception as e:
            self.logger.log_performance_metric(
                "plugin_initialization_failed",
                1,
                {"plugin": self.name, "error": str(e)},
            )
            return False

    def cleanup(self) -> None:
        """Cleanup tool plugin resources."""
        self.tools.clear()


class MiddlewarePlugin(PluginBase):
    """Plugin for adding middleware components."""

    def __init__(self, name: str, version: str = "1.0.0"):
        super().__init__(name, version)
        self.middleware_classes: list[type] = []

    def add_middleware(self, middleware_class: type) -> None:
        """Add middleware class to the plugin."""
        self.middleware_classes.append(middleware_class)

    def get_middleware_classes(self) -> list[type]:
        """Get all middleware classes provided by this plugin."""
        return self.middleware_classes.copy()

    def initialize(self, config: dict[str, Any]) -> bool:
        """Initialize middleware plugin."""
        try:
            self.logger.log_performance_metric(
                "plugin_initialized", 1, {"plugin": self.name, "type": "middleware"}
            )
            return True
        except Exception as e:
            self.logger.log_performance_metric(
                "plugin_initialization_failed",
                1,
                {"plugin": self.name, "error": str(e)},
            )
            return False

    def cleanup(self) -> None:
        """Cleanup middleware plugin resources."""
        self.middleware_classes.clear()


class ParserPlugin(PluginBase):
    """Plugin for adding custom parsers."""

    def __init__(self, name: str, version: str = "1.0.0"):
        super().__init__(name, version)
        self.parsers: dict[str, Callable] = {}

    def add_parser(self, format_name: str, parser_function: Callable) -> None:
        """Add a parser for specific format."""
        self.parsers[format_name] = parser_function

    def get_parsers(self) -> dict[str, Callable]:
        """Get all parsers provided by this plugin."""
        return self.parsers.copy()

    def parse(self, format_name: str, data: Any) -> Any:
        """Parse data using specified format."""
        parser = self.parsers.get(format_name)
        if not parser:
            raise ValueError(f"Parser for format '{format_name}' not found")

        return parser(data)

    def initialize(self, config: dict[str, Any]) -> bool:
        """Initialize parser plugin."""
        try:
            self.logger.log_performance_metric(
                "plugin_initialized", 1, {"plugin": self.name, "type": "parser"}
            )
            return True
        except Exception as e:
            self.logger.log_performance_metric(
                "plugin_initialization_failed",
                1,
                {"plugin": self.name, "error": str(e)},
            )
            return False

    def cleanup(self) -> None:
        """Cleanup parser plugin resources."""
        self.parsers.clear()


class PluginManager:
    """Manager for loading and managing plugins."""

    def __init__(self):
        self.plugins: dict[str, PluginBase] = {}
        self.plugin_dirs: list[Path] = []
        self.logger = get_structured_logger()

        # Add default plugin directories
        self.add_plugin_dir(Path(__file__).parent.parent / "plugins")
        self.add_plugin_dir(Path.cwd() / "plugins")

    def add_plugin_dir(self, directory: Path) -> None:
        """Add a directory to search for plugins."""
        if directory.exists():
            self.plugin_dirs.append(directory)
            self.logger.log_performance_metric("plugin_dir_added", 1, {"directory": str(directory)})

    def discover_plugins(self) -> list[str]:
        """Discover available plugins in plugin directories."""
        discovered_plugins = []

        for plugin_dir in self.plugin_dirs:
            if not plugin_dir.exists():
                continue

            for item in plugin_dir.iterdir():
                if item.is_dir() and (item / "__init__.py").exists():
                    discovered_plugins.append(item.name)
                elif item.is_file() and item.suffix == ".py" and item.name != "__init__.py":
                    discovered_plugins.append(item.stem)

        return discovered_plugins

    def load_plugin(self, plugin_name: str, config: Optional[dict[str, Any]] = None) -> bool:
        """Load a plugin by name."""
        if plugin_name in self.plugins:
            self.logger.log_performance_metric("plugin_already_loaded", 1, {"plugin": plugin_name})
            return True

        config = config or {}

        # Try to import plugin
        try:
            # First try direct import
            plugin_module = importlib.import_module(f"src.plugins.{plugin_name}")
        except ImportError:
            try:
                # Try loading from plugin directories
                plugin_module = None
                for plugin_dir in self.plugin_dirs:
                    if (plugin_dir / f"{plugin_name}.py").exists():
                        spec = importlib.util.spec_from_file_location(
                            plugin_name, plugin_dir / f"{plugin_name}.py"
                        )
                        if spec and spec.loader:
                            plugin_module = importlib.util.module_from_spec(spec)
                            sys.modules[plugin_name] = plugin_module
                            spec.loader.exec_module(plugin_module)
                            break
                    elif (plugin_dir / plugin_name / "__init__.py").exists():
                        spec = importlib.util.spec_from_file_location(
                            plugin_name, plugin_dir / plugin_name / "__init__.py"
                        )
                        if spec and spec.loader:
                            plugin_module = importlib.util.module_from_spec(spec)
                            sys.modules[plugin_name] = plugin_module
                            spec.loader.exec_module(plugin_module)
                            break

                if not plugin_module:
                    raise ImportError(f"Plugin {plugin_name} not found")

            except Exception as e:
                self.logger.log_performance_metric(
                    "plugin_load_failed", 1, {"plugin": plugin_name, "error": str(e)}
                )
                return False

        # Find plugin class
        plugin_class = None
        for attr_name in dir(plugin_module):
            attr = getattr(plugin_module, attr_name)
            if inspect.isclass(attr) and issubclass(attr, PluginBase) and attr != PluginBase:
                plugin_class = attr
                break

        if not plugin_class:
            self.logger.log_performance_metric("plugin_class_not_found", 1, {"plugin": plugin_name})
            return False

        # Instantiate and initialize plugin
        try:
            plugin_instance = plugin_class(plugin_name)
            if plugin_instance.initialize(config):
                self.plugins[plugin_name] = plugin_instance
                plugin_instance.enable()
                self.logger.log_performance_metric("plugin_loaded", 1, {"plugin": plugin_name})
                return True
            else:
                self.logger.log_performance_metric(
                    "plugin_initialization_failed", 1, {"plugin": plugin_name}
                )
                return False

        except Exception as e:
            self.logger.log_performance_metric(
                "plugin_instantiation_failed",
                1,
                {"plugin": plugin_name, "error": str(e)},
            )
            return False

    def unload_plugin(self, plugin_name: str) -> bool:
        """Unload a plugin."""
        if plugin_name not in self.plugins:
            return False

        plugin = self.plugins[plugin_name]
        try:
            plugin.disable()
            plugin.cleanup()
            del self.plugins[plugin_name]
            self.logger.log_performance_metric("plugin_unloaded", 1, {"plugin": plugin_name})
            return True
        except Exception as e:
            self.logger.log_performance_metric(
                "plugin_unload_failed", 1, {"plugin": plugin_name, "error": str(e)}
            )
            return False

    def get_plugin(self, plugin_name: str) -> Optional[PluginBase]:
        """Get a loaded plugin by name."""
        return self.plugins.get(plugin_name)

    def list_plugins(self) -> dict[str, dict[str, Any]]:
        """List all loaded plugins with their info."""
        return {
            name: {
                "name": plugin.name,
                "version": plugin.version,
                "enabled": plugin.is_enabled(),
                "type": type(plugin).__name__,
            }
            for name, plugin in self.plugins.items()
        }

    def get_tools_from_plugins(self) -> dict[str, Callable]:
        """Get all tools from loaded plugins."""
        all_tools = {}

        for plugin in self.plugins.values():
            if isinstance(plugin, ToolPlugin):
                all_tools.update(plugin.get_tools())

        return all_tools

    def get_middleware_from_plugins(self) -> list[type]:
        """Get all middleware classes from loaded plugins."""
        all_middleware = []

        for plugin in self.plugins.values():
            if isinstance(plugin, MiddlewarePlugin):
                all_middleware.extend(plugin.get_middleware_classes())

        return all_middleware

    def get_parsers_from_plugins(self) -> dict[str, Callable]:
        """Get all parsers from loaded plugins."""
        all_parsers = {}

        for plugin in self.plugins.values():
            if isinstance(plugin, ParserPlugin):
                all_parsers.update(plugin.get_parsers())

        return all_parsers

    def cleanup(self) -> None:
        """Cleanup all loaded plugins."""
        for plugin in list(self.plugins.values()):
            try:
                plugin.disable()
                plugin.cleanup()
            except Exception as e:
                self.logger.log_performance_metric(
                    "plugin_cleanup_failed", 1, {"plugin": plugin.name, "error": str(e)}
                )

        self.plugins.clear()
        self.logger.log_performance_metric("plugin_manager_cleanup", 1)


# Global plugin manager
_plugin_manager = PluginManager()


def get_plugin_manager() -> PluginManager:
    """Get global plugin manager."""
    return _plugin_manager


def load_builtin_plugins():
    """Load built-in plugins."""
    # This will be expanded as we create more plugins
    pass


# Example plugin: Custom Parsers
class CustomParsersPlugin(ParserPlugin):
    """Built-in plugin providing additional parsers."""

    def __init__(self):
        super().__init__("custom_parsers", "1.0.0")

    def initialize(self, config: dict[str, Any]) -> bool:
        """Initialize custom parsers plugin."""
        # Add custom parsers
        self.add_parser("yaml", self.parse_yaml)
        self.add_parser("toml", self.parse_toml)
        self.add_parser("xml", self.parse_xml)

        return True

    def parse_yaml(self, data: str) -> dict[str, Any]:
        """Parse YAML data."""
        try:
            import yaml

            return yaml.safe_load(data)
        except ImportError:
            raise ImportError("PyYAML not installed. Install with: pip install PyYAML")

    def parse_toml(self, data: str) -> dict[str, Any]:
        """Parse TOML data."""
        try:
            import tomllib

            return tomllib.loads(data)
        except ImportError:
            try:
                import tomli

                return tomli.loads(data)
            except ImportError:
                raise ImportError("tomli not installed. Install with: pip install tomli")

    def parse_xml(self, data: str) -> dict[str, Any]:
        """Parse XML data to dictionary."""
        try:
            import xml.etree.ElementTree as ET

            root = ET.fromstring(data)
            return self._xml_to_dict(root)
        except Exception as e:
            raise ValueError(f"Invalid XML: {e}")

    def _xml_to_dict(self, element):
        """Convert XML element to dictionary."""
        result = {}

        # Add attributes
        if element.attrib:
            result["@attributes"] = element.attrib

        # Add text content
        if element.text and element.text.strip():
            result["#text"] = element.text.strip()

        # Add children
        children = list(element)
        if children:
            for child in children:
                child_dict = self._xml_to_dict(child)
                if child.tag in result:
                    if not isinstance(result[child.tag], list):
                        result[child.tag] = [result[child.tag]]
                    result[child.tag].append(child_dict)
                else:
                    result[child.tag] = child_dict

        return result


# Initialize built-in plugins
def initialize_builtin_plugins():
    """Initialize built-in plugins."""
    manager = get_plugin_manager()

    # Load custom parsers plugin
    manager.load_plugin("custom_parsers")


# Initialize on import
initialize_builtin_plugins()
