"""
Base Plugin Interface

All project plugins must inherit from this base class.
This ensures consistency across different project types.
"""

import logging
from abc import ABC, abstractmethod
from typing import Any

logger = logging.getLogger(__name__)


class BasePlugin(ABC):
    """
    Base class for all project plugins.

    Each plugin represents a specific project type (WordPress, Supabase, etc.)
    and provides MCP tools for managing that project.
    """

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

        **Option B Architecture (New)**:
            plugin = WordPressPlugin(config)
            # project_id extracted from config or generated

        **Legacy Architecture**:
            plugin = WordPressPlugin(config, project_id="wordpress_site1")
            # Explicit project_id

        Args:
            config: Project-specific configuration (URLs, credentials, etc.)
            project_id: Optional unique identifier (auto-generated if not provided)
        """
        # Auto-generate project_id if not provided (Option B)
        if project_id is None:
            # Generate from class name + config
            class_name = self.__class__.__name__.lower().replace("plugin", "")
            site_id = config.get("site_id", config.get("url", "unknown"))
            # Simple hash for unique ID
            import hashlib

            if isinstance(site_id, str):
                hash_suffix = hashlib.md5(site_id.encode()).hexdigest()[:8]
                project_id = f"{class_name}_{hash_suffix}"
            else:
                import time

                project_id = f"{class_name}_{int(time.time())}"

        self.project_id = project_id
        self.config = config
        self.logger = logging.getLogger(f"{self.__class__.__name__}.{project_id}")

        # Validate required configuration
        self._validate_config()

        self.logger.info(f"Initialized plugin for project: {project_id}")

    @abstractmethod
    def get_plugin_name(self) -> str:
        """
        Return the plugin type name (e.g., 'wordpress', 'supabase').

        Returns:
            str: Plugin type identifier
        """
        pass

    @staticmethod
    def get_tool_specifications() -> list[dict[str, Any]]:
        """
        Return tool specifications for Option B architecture (ToolGenerator).

        This is a STATIC method that returns tool specifications without
        needing a plugin instance. ToolGenerator uses these specifications
        to create unified tools with site parameter routing.

        Each specification should contain:
        - name: Tool name (without site prefix)
        - method_name: Method to call on plugin instance
        - description: What the tool does
        - schema: JSON Schema for input validation (without site parameter)
        - scope: Required scope (read, write, admin)

        Example:
            [
                {
                    "name": "list_posts",
                    "method_name": "list_posts",
                    "description": "List WordPress posts",
                    "schema": {...},
                    "scope": "read"
                }
            ]

        Returns:
            List[Dict]: List of tool specifications for ToolGenerator

        Note:
            Override this in subclasses for Option B architecture.
            If not implemented, returns empty list (legacy plugins).
        """
        return []

    def get_tools(self) -> list[dict[str, Any]]:
        """
        Return list of MCP tools provided by this plugin (LEGACY).

        **DEPRECATED in Option B Architecture**
        This method is kept for backward compatibility with legacy per-site
        tool architecture. New plugins should implement get_tool_specifications()
        instead, which is used by ToolGenerator for unified tools.

        Legacy per-site tools format:
        - name: Tool name (prefixed with plugin type and project_id)
        - description: What the tool does
        - inputSchema: JSON Schema for input validation
        - handler: Async function that implements the tool

        Returns:
            List[Dict]: List of tool definitions (empty for Option B plugins)

        Note:
            Option B plugins should return [] here as tools are registered
            via get_tool_specifications() + ToolGenerator instead.
        """
        return []

    def _validate_config(self) -> None:
        """
        Validate that required configuration keys are present.
        Override in subclasses to add specific validation.
        """
        required_keys = self.get_required_config_keys()
        missing_keys = [key for key in required_keys if key not in self.config]

        if missing_keys:
            raise ValueError(
                f"Missing required configuration keys for {self.project_id}: "
                f"{', '.join(missing_keys)}"
            )

    def get_required_config_keys(self) -> list[str]:
        """
        Return list of required configuration keys.
        Override in subclasses.

        Returns:
            List[str]: Required config keys
        """
        return []

    async def health_check(self) -> dict[str, Any]:
        """
        Check if the project is accessible and healthy.
        Override in subclasses to implement specific health checks.

        Returns:
            Dict with 'healthy' (bool) and 'message' (str) keys
        """
        return {"healthy": True, "message": "Health check not implemented for this plugin"}

    async def probe_credential_capabilities(self) -> dict[str, Any]:
        """F.7e — report what the saved credential can actually do.

        Plugins should override this to call the upstream service
        (WordPress companion ``/capabilities``, WooCommerce
        ``system_status``, Gitea ``/user`` header scopes, Coolify
        ``/api/v1/teams/0``, etc.) and return the subset of capability
        names the caller's token actually grants.

        The base implementation returns a well-shaped "probe not
        available" payload so callers can handle all plugin types
        uniformly without special-casing the ones that haven't
        implemented a probe yet.

        Returns:
            Dict with:
                probe_available: bool — True if the plugin knows how to
                    query its upstream for capabilities.
                granted: list[str] — the capabilities the credential
                    actually grants (subset of the universe the plugin
                    understands). Empty when ``probe_available=False``.
                source: str — which endpoint / header the list came from;
                    ``"unavailable"`` when no probe is wired.
                reason: str | None — optional human-readable diagnostic
                    when ``probe_available=False`` or when the call
                    failed in a way that still produced a usable answer.
        """
        return {
            "probe_available": False,
            "granted": [],
            "source": "unavailable",
            "reason": "probe_not_implemented",
        }

    def get_project_info(self) -> dict[str, Any]:
        """
        Return basic information about this project instance.

        Returns:
            Dict with project metadata
        """
        return {
            "project_id": self.project_id,
            "plugin_type": self.get_plugin_name(),
            "config_keys": list(self.config.keys()),
        }

    def _create_tool_name(self, action: str) -> str:
        """
        Create a standardized tool name for per-site tools.

        FORMAT: {plugin_type}_{site_id}_{action}
        Example: wordpress_site1_list_posts

        This is used for backward-compatible per-site tools.
        Unified tools (wordpress_list_posts) are created separately by UnifiedToolGenerator.

        Args:
            action: The action this tool performs

        Returns:
            str: Formatted tool name with project_id for per-site tools
        """
        # Extract just the site_id from project_id (e.g., "site1" from "wordpress_site1")
        # project_id format is {plugin_type}_{site_id}
        site_id = self.project_id.replace(f"{self.get_plugin_name()}_", "")
        return f"{self.get_plugin_name()}_{site_id}_{action}"

    def _format_error_response(self, error: Exception, action: str) -> str:
        """
        Format an error into a user-friendly message.

        Args:
            error: The exception that occurred
            action: The action that was being performed

        Returns:
            str: Formatted error message
        """
        error_msg = f"Error performing {action} on {self.project_id}: {str(error)}"
        self.logger.error(error_msg, exc_info=True)
        return error_msg

    def _format_success_response(self, data: Any, action: str) -> str:
        """
        Format a successful response.

        Args:
            data: The data to return
            action: The action that was performed

        Returns:
            str: Formatted success message
        """
        if isinstance(data, (dict, list)):
            import json

            return json.dumps(data, indent=2, ensure_ascii=False)
        return str(data)


class PluginRegistry:
    """
    Registry for managing available plugin types.
    """

    def __init__(self):
        self._plugin_classes: dict[str, type] = {}
        self.logger = logging.getLogger("PluginRegistry")

    def register(self, plugin_type: str, plugin_class: type) -> None:
        """
        Register a plugin class.

        Args:
            plugin_type: Type identifier (e.g., 'wordpress')
            plugin_class: Plugin class (must inherit from BasePlugin)
        """
        if not issubclass(plugin_class, BasePlugin):
            raise TypeError(f"{plugin_class} must inherit from BasePlugin")

        self._plugin_classes[plugin_type] = plugin_class
        self.logger.info(f"Registered plugin type: {plugin_type}")

    def create_instance(
        self, plugin_type: str, project_id: str, config: dict[str, Any]
    ) -> BasePlugin:
        """
        Create a plugin instance.

        **Option B Compatible**: Uses new BasePlugin signature (config, project_id)

        Args:
            plugin_type: Type of plugin to create
            project_id: Unique project identifier
            config: Project configuration

        Returns:
            BasePlugin: Instantiated plugin

        Raises:
            KeyError: If plugin_type is not registered
        """
        if plugin_type not in self._plugin_classes:
            raise KeyError(f"Unknown plugin type: {plugin_type}")

        plugin_class = self._plugin_classes[plugin_type]
        # Option B signature: config first, project_id optional
        return plugin_class(config, project_id=project_id)

    def get_registered_types(self) -> list[str]:
        """Get list of registered plugin types."""
        return list(self._plugin_classes.keys())

    def is_registered(self, plugin_type: str) -> bool:
        """Check if a plugin type is registered."""
        return plugin_type in self._plugin_classes
