"""
Rendering Engine Plugin Support

Extends the existing MarkiTect plugin system to support UI rendering engines
for different output modes (edit, view, print, etc.).
"""

from abc import abstractmethod
from typing import Dict, List, Optional, Any
from pathlib import Path
import json

from .base import BasePlugin, PluginType, PluginMetadata


class RenderingEnginePlugin(BasePlugin):
    """Base class for rendering engine plugins."""

    def __init__(self):
        """Initialize rendering engine plugin."""
        # Set plugin type to a new RENDERING type
        if not hasattr(PluginType, 'RENDERING'):
            # Add RENDERING type if it doesn't exist
            PluginType.RENDERING = "rendering"

        super().__init__()

    @abstractmethod
    def get_supported_modes(self) -> List[str]:
        """
        Return supported rendering modes.

        Returns:
            List of mode strings (e.g., ['edit', 'view', 'print'])
        """
        pass

    @abstractmethod
    def get_required_assets(self) -> Dict[str, List[str]]:
        """
        Return required assets by type.

        Returns:
            Dict with keys like 'js', 'css', 'images', each containing
            list of relative paths within the plugin directory.

        Example:
            {
                'js': ['static/js/main.js', 'static/js/config-loader.js'],
                'css': ['static/css/editor.css'],
                'images': ['images/icons/edit.png']
            }
        """
        pass

    @abstractmethod
    def render_document(self,
                       content: str,
                       mode: str,
                       config: 'RenderingConfig') -> str:
        """
        Render markdown content to HTML using this engine.

        Args:
            content: Markdown content to render
            mode: Rendering mode ('edit', 'view', etc.)
            config: Rendering configuration with asset paths

        Returns:
            Complete HTML document
        """
        pass

    def get_template_path(self) -> Optional[Path]:
        """Return path to engine's HTML template file (optional)."""
        return None

    def validate_mode(self, mode: str) -> bool:
        """Check if mode is supported by this engine."""
        return mode in self.get_supported_modes()

    def get_asset_manifest(self) -> Dict[str, Any]:
        """
        Get complete asset manifest for this rendering engine.

        Returns:
            Manifest dict with asset information for deployment
        """
        return {
            'name': self.metadata.name,
            'version': self.metadata.version,
            'modes': self.get_supported_modes(),
            'assets': self.get_required_assets(),
            'template': str(self.get_template_path()) if self.get_template_path() else None
        }


class RenderingConfig:
    """Configuration for rendering engine asset management and deployment."""

    def __init__(self,
                 asset_base_url: str = "_markitect",
                 development_mode: bool = False,
                 plugin_source_dirs: Optional[Dict[str, Path]] = None,
                 output_directory: Optional[Path] = None):
        """
        Initialize rendering configuration.

        Args:
            asset_base_url: Base URL/path for assets (e.g., "_markitect")
            development_mode: If True, serve from source directories
            plugin_source_dirs: Map of plugin_name -> source directory path
            output_directory: Target directory for asset deployment
        """
        self.asset_base_url = asset_base_url
        self.development_mode = development_mode
        self.plugin_source_dirs = plugin_source_dirs or {}
        self.output_directory = output_directory
        self._asset_cache = {}

    def get_asset_url(self, plugin_name: str, asset_path: str) -> str:
        """
        Get URL path for a plugin asset.

        Args:
            plugin_name: Name of the plugin (e.g., 'testdrive-jsui')
            asset_path: Relative path within plugin (e.g., 'static/js/main.js')

        Returns:
            Full asset URL path
        """
        if self.development_mode and plugin_name in self.plugin_source_dirs:
            # Development: serve directly from source directory
            source_dir = self.plugin_source_dirs[plugin_name]
            return f"file://{source_dir}/{asset_path}"
        else:
            # Production: serve from _markitect/plugins/
            return f"{self.asset_base_url}/plugins/{plugin_name}/{asset_path}"

    def get_plugin_asset_dir(self, plugin_name: str) -> Path:
        """Get the asset directory path for a plugin."""
        if self.output_directory:
            return self.output_directory / self.asset_base_url / "plugins" / plugin_name
        else:
            return Path(self.asset_base_url) / "plugins" / plugin_name

    def to_json_config(self, plugin_name: str) -> str:
        """
        Generate JSON configuration for JavaScript consumption.

        Args:
            plugin_name: Name of the plugin for which to generate config

        Returns:
            JSON string suitable for embedding in HTML
        """
        config_data = {
            'pluginName': plugin_name,
            'assetBaseUrl': self.asset_base_url,
            'developmentMode': self.development_mode,
            'pluginAssetDir': f"{self.asset_base_url}/plugins/{plugin_name}"
        }

        if plugin_name in self.plugin_source_dirs:
            config_data['sourceDir'] = str(self.plugin_source_dirs[plugin_name])

        return json.dumps(config_data, indent=2)


class RenderingEngineManager:
    """Manager for rendering engine plugins."""

    def __init__(self, plugin_manager):
        """
        Initialize with existing plugin manager.

        Args:
            plugin_manager: Main MarkiTect plugin manager instance
        """
        self.plugin_manager = plugin_manager
        self._engines: Dict[str, RenderingEnginePlugin] = {}
        self._discover_rendering_engines()

    def _discover_rendering_engines(self):
        """Discover rendering engine plugins."""
        # First, try to load plugins from main plugin manager
        all_plugins = self.plugin_manager.discover_plugins()

        for plugin_name, plugin_info in all_plugins.items():
            if plugin_info.get('type') == 'rendering':
                try:
                    # Load the plugin
                    plugin_instance = self.plugin_manager.load_plugin(plugin_name)
                    if isinstance(plugin_instance, RenderingEnginePlugin):
                        self._engines[plugin_name] = plugin_instance
                        print(f"✅ Discovered rendering engine: {plugin_name}")
                except Exception as e:
                    print(f"⚠️ Failed to load rendering engine {plugin_name}: {e}")

        # Additionally, try to directly import and register known rendering engines
        self._register_builtin_rendering_engines()

    def _register_builtin_rendering_engines(self):
        """Register built-in rendering engines directly."""
        try:
            # Import and register testdrive-jsui engine
            from .testdrive_jsui import TestDriveJSUIEngine
            engine = TestDriveJSUIEngine()
            self._engines[engine.metadata.name] = engine
            print(f"✅ Registered built-in rendering engine: {engine.metadata.name}")
        except ImportError as e:
            print(f"⚠️ Could not import testdrive-jsui engine: {e}")
        except Exception as e:
            print(f"⚠️ Failed to register testdrive-jsui engine: {e}")

    def get_engine(self, name: str) -> Optional[RenderingEnginePlugin]:
        """Get a rendering engine by name."""
        return self._engines.get(name)

    def list_engines(self) -> List[str]:
        """List all registered engine names."""
        return list(self._engines.keys())

    def get_engines_for_mode(self, mode: str) -> List[str]:
        """Get engine names that support a specific mode."""
        return [name for name, engine in self._engines.items()
                if engine.validate_mode(mode)]

    def deploy_engine_assets(self,
                           engine_name: str,
                           config: RenderingConfig) -> Dict[str, str]:
        """
        Deploy assets for a rendering engine.

        Args:
            engine_name: Name of the rendering engine
            config: Rendering configuration

        Returns:
            Dict mapping asset types to deployment paths
        """
        engine = self.get_engine(engine_name)
        if not engine:
            raise ValueError(f"Rendering engine '{engine_name}' not found")

        if config.development_mode:
            # In development mode, just return source paths
            return {'status': 'development_mode', 'source': 'plugin_directory'}

        if not config.output_directory:
            return {'status': 'no_output_directory'}

        # Production deployment: copy assets to output directory
        import shutil
        deployed_assets = {}
        target_dir = config.get_plugin_asset_dir(engine_name)
        required_assets = engine.get_required_assets()

        # Create target directory
        target_dir.mkdir(parents=True, exist_ok=True)

        # Deploy each asset type
        for asset_type, asset_list in required_assets.items():
            if asset_type == 'external':
                # Skip external assets (CDN resources)
                continue

            deployed_files = []

            # Determine source directory for assets
            source_base = self._get_plugin_source_dir(engine_name)
            if not source_base or not source_base.exists():
                print(f"⚠️ Plugin source directory not found for {engine_name}: {source_base}")
                continue

            for asset_path in asset_list:
                source_file = source_base / asset_path
                target_file = target_dir / asset_path

                # Create parent directories
                target_file.parent.mkdir(parents=True, exist_ok=True)

                if source_file.exists():
                    try:
                        shutil.copy2(source_file, target_file)
                        deployed_files.append(str(target_file))
                        print(f"📄 Deployed: {asset_path}")
                    except Exception as e:
                        print(f"⚠️ Failed to deploy {asset_path}: {e}")
                else:
                    print(f"⚠️ Asset not found: {source_file}")

            if deployed_files:
                deployed_assets[asset_type] = deployed_files

        return deployed_assets

    def _get_plugin_source_dir(self, engine_name: str) -> Optional[Path]:
        """
        Get the source directory for a plugin.
        Uses plugin self-declaration if available, no hardcoded paths.
        """
        engine = self.get_engine(engine_name)
        if not engine:
            return None

        # Use plugin's self-declared source directory if available
        if hasattr(engine, 'get_plugin_source_dir'):
            try:
                source_dir = engine.get_plugin_source_dir()
                if source_dir and source_dir.exists():
                    return source_dir
            except Exception as e:
                print(f"⚠️ Error getting plugin source dir for {engine_name}: {e}")

        return None