"""
Base classes and interfaces for MarkiTect plugins.

This module defines the core plugin architecture that all plugins must implement.
"""

from abc import ABC, abstractmethod
from enum import Enum
from typing import Dict, Any, Optional, List, Union
from pathlib import Path
import inspect


class PluginType(Enum):
    """Types of plugins supported by MarkiTect."""
    PROCESSOR = "processor"           # Content processors (markdown, etc.)
    FORMATTER = "formatter"           # Output formatters (JSON, YAML, etc.)
    VALIDATOR = "validator"           # Content validators
    EXPORTER = "exporter"            # Export handlers (PDF, HTML, etc.)
    GENERATOR = "generator"          # Content generators (templates, stubs, etc.)
    IMPORTER = "importer"            # Import handlers (various formats)
    TRANSFORMER = "transformer"      # Content transformers
    EXTENSION = "extension"          # General extensions
    BACKEND = "backend"              # Storage/API backends
    COMMAND = "command"              # CLI command extensions


class PluginMetadata:
    """Metadata about a plugin."""

    def __init__(self,
                 name: str,
                 version: str,
                 description: str,
                 author: str = "",
                 plugin_type: PluginType = PluginType.EXTENSION,
                 dependencies: List[str] = None,
                 markitect_version: str = ">=0.1.0"):
        self.name = name
        self.version = version
        self.description = description
        self.author = author
        self.plugin_type = plugin_type
        self.dependencies = dependencies or []
        self.markitect_version = markitect_version


class BasePlugin(ABC):
    """Abstract base class for all MarkiTect plugins."""

    def __init__(self, config: Dict[str, Any] = None):
        """
        Initialize plugin with configuration.

        Args:
            config: Plugin-specific configuration dictionary
        """
        self.config = config or {}
        self._metadata = None
        self._initialized = False

    @property
    @abstractmethod
    def metadata(self) -> PluginMetadata:
        """Return plugin metadata."""
        pass

    def initialize(self) -> bool:
        """
        Initialize the plugin. Called after plugin is loaded.

        Returns:
            True if initialization successful, False otherwise
        """
        try:
            self._initialize()
            self._initialized = True
            return True
        except Exception:
            return False

    def _initialize(self) -> None:
        """
        Override this method to implement plugin-specific initialization.
        Default implementation does nothing.
        """
        pass

    def cleanup(self) -> None:
        """
        Cleanup plugin resources. Called when plugin is unloaded.
        Override this method to implement cleanup logic.
        """
        pass

    @property
    def is_initialized(self) -> bool:
        """Check if plugin is initialized."""
        return self._initialized

    def validate_config(self) -> List[str]:
        """
        Validate plugin configuration.

        Returns:
            List of validation error messages (empty if valid)
        """
        return []


class ProcessorPlugin(BasePlugin):
    """Base class for content processor plugins."""

    @abstractmethod
    def process(self, content: str, **kwargs) -> str:
        """
        Process content and return processed result.

        Args:
            content: Input content to process
            **kwargs: Additional processing parameters

        Returns:
            Processed content
        """
        pass

    def can_process(self, content: str, **kwargs) -> bool:
        """
        Check if this processor can handle the given content.

        Args:
            content: Content to check
            **kwargs: Additional context

        Returns:
            True if processor can handle content
        """
        return True


class FormatterPlugin(BasePlugin):
    """Base class for output formatter plugins."""

    @abstractmethod
    def format(self, data: Any, **kwargs) -> str:
        """
        Format data to string representation.

        Args:
            data: Data to format
            **kwargs: Formatting options

        Returns:
            Formatted string
        """
        pass

    @abstractmethod
    def get_file_extension(self) -> str:
        """
        Get the file extension for this format.

        Returns:
            File extension (e.g., '.json', '.yaml')
        """
        pass


class ValidatorPlugin(BasePlugin):
    """Base class for content validator plugins."""

    @abstractmethod
    def validate(self, content: str, **kwargs) -> List[str]:
        """
        Validate content and return list of errors.

        Args:
            content: Content to validate
            **kwargs: Validation options

        Returns:
            List of validation error messages (empty if valid)
        """
        pass


class ExporterPlugin(BasePlugin):
    """Base class for export handler plugins."""

    @abstractmethod
    def export(self, data: Any, output_path: Path, **kwargs) -> bool:
        """
        Export data to file.

        Args:
            data: Data to export
            output_path: Output file path
            **kwargs: Export options

        Returns:
            True if export successful
        """
        pass

    @abstractmethod
    def get_supported_formats(self) -> List[str]:
        """
        Get list of supported export formats.

        Returns:
            List of format names (e.g., ['pdf', 'html'])
        """
        pass


class CommandPlugin(BasePlugin):
    """Base class for CLI command extension plugins."""

    @abstractmethod
    def get_commands(self) -> Dict[str, Any]:
        """
        Get Click commands provided by this plugin.

        Returns:
            Dictionary mapping command names to Click command objects
        """
        pass

    def get_command_group_name(self) -> Optional[str]:
        """
        Get the command group name if commands should be grouped.

        Returns:
            Group name or None for top-level commands
        """
        return None


# Plugin discovery and loading utilities

def get_plugin_class_from_module(module, plugin_type: PluginType = None) -> List[type]:
    """
    Discover plugin classes in a module.

    Args:
        module: Python module to search
        plugin_type: Optional plugin type filter

    Returns:
        List of plugin classes found
    """
    plugin_classes = []

    for name, obj in inspect.getmembers(module, inspect.isclass):
        if (issubclass(obj, BasePlugin) and
            obj != BasePlugin and
            not inspect.isabstract(obj)):

            # Check plugin type if specified
            if plugin_type:
                try:
                    instance = obj()
                    if instance.metadata.plugin_type == plugin_type:
                        plugin_classes.append(obj)
                except Exception:
                    # Skip if we can't instantiate or get metadata
                    continue
            else:
                plugin_classes.append(obj)

    return plugin_classes