"""
Simple plugin system for NoteDiscovery
Plugins can hook into events like note save, delete, etc.
"""

import os
import json
import importlib.util
from pathlib import Path
from typing import List, Dict, Callable


class Plugin:
    """Base plugin class"""
    
    def __init__(self):
        self.name = "Base Plugin"
        self.version = "1.0.0"
        self.enabled = False
    
    def on_note_save(self, note_path: str, content: str) -> str | None:
        """
        Called when a note is being saved.
        Can optionally transform content before writing to disk (e.g., encrypt).
        
        Args:
            note_path: Path to the note being saved
            content: Content to be saved
            
        Returns:
            Transformed content, or None to keep original
        """
        return None
    
    def on_note_delete(self, note_path: str):
        """Called when a note is deleted"""
        pass
    
    def on_search(self, query: str, results: List[Dict]):
        """Called after a search is performed"""
        pass
    
    def on_note_create(self, note_path: str, initial_content: str) -> str:
        """
        Called when a new note is created (before first save).
        Can modify and return the initial content.
        
        Args:
            note_path: Path to the new note
            initial_content: The initial content for the note
            
        Returns:
            Modified content (or return initial_content unchanged)
        """
        return initial_content
    
    def on_note_load(self, note_path: str, content: str) -> str | None:
        """
        Called when a note is loaded from disk.
        Can optionally transform content before displaying (e.g., decrypt).
        
        Args:
            note_path: Path to the loaded note
            content: Content loaded from disk
            
        Returns:
            Transformed content, or None to keep original
        """
        return None
    
    def on_app_startup(self):
        """
        Called when the application starts up.
        Useful for initialization, sync, health checks, etc.
        """
        pass


class PluginManager:
    """Manages loading and execution of plugins"""
    
    def __init__(self, plugins_dir: str):
        self.plugins_dir = Path(plugins_dir)
        self.plugins: Dict[str, Plugin] = {}
        self.config_file = self.plugins_dir / "plugin_config.json"
        self.load_plugins()
        self._apply_saved_state()
        # Save config to create/update the file with current states
        if self.plugins:  # Only save if there are plugins loaded
            self._save_config()
    
    def load_plugins(self):
        """Load all plugins from the plugins directory"""
        if not self.plugins_dir.exists():
            self.plugins_dir.mkdir(parents=True, exist_ok=True)
            self._create_example_plugin()
            return
        
        for plugin_file in self.plugins_dir.glob("*.py"):
            if plugin_file.stem.startswith("_"):
                continue
            
            try:
                spec = importlib.util.spec_from_file_location(
                    plugin_file.stem, plugin_file
                )
                if spec and spec.loader:
                    module = importlib.util.module_from_spec(spec)
                    spec.loader.exec_module(module)
                    
                    # Look for Plugin class in module
                    if hasattr(module, 'Plugin'):
                        plugin = module.Plugin()
                        self.plugins[plugin_file.stem] = plugin
            except Exception as e:
                print(f"Failed to load plugin {plugin_file.stem}: {e}")
    
    def _create_example_plugin(self):
        """Create an example plugin to show developers how to build plugins"""
        example_plugin = '''"""
Example Plugin for NoteDiscovery
This plugin demonstrates how to create custom plugins.
"""

class Plugin:
    def __init__(self):
        self.name = "Example Plugin"
        self.version = "1.0.0"
        self.enabled = True
    
    def on_note_save(self, note_path: str, content: str):
        """This runs every time a note is saved"""
        print(f"✓ Plugin: Note saved - {note_path}")
        
        # Example: Automatically add tags to notes
        # if '#todo' in content:
        #     print("  → Found TODO tag!")
    
    def on_note_delete(self, note_path: str):
        """This runs when a note is deleted"""
        print(f"✗ Plugin: Note deleted - {note_path}")
    
    def on_search(self, query: str, results: list):
        """This runs after a search is performed"""
        print(f"🔍 Plugin: Search performed for '{query}' ({len(results)} results)")
'''
        example_path = self.plugins_dir / "example_plugin.py"
        with open(example_path, 'w', encoding='utf-8') as f:
            f.write(example_plugin)
    
    def list_plugins(self) -> List[Dict]:
        """Get a list of all loaded plugins"""
        return [
            {
                "id": plugin_id,
                "name": plugin.name,
                "version": plugin.version,
                "enabled": plugin.enabled
            }
            for plugin_id, plugin in self.plugins.items()
        ]
    
    def _load_config(self) -> Dict[str, bool]:
        """Load plugin configuration from JSON file"""
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except Exception as e:
                print(f"Failed to load plugin config: {e}")
        return {}
    
    def _save_config(self):
        """Save current plugin states to JSON file"""
        try:
            config = {
                plugin_id: plugin.enabled
                for plugin_id, plugin in self.plugins.items()
            }
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(config, f, indent=2)
        except Exception as e:
            print(f"Failed to save plugin config: {e}")
    
    def _apply_saved_state(self):
        """Apply saved plugin states after loading plugins"""
        saved_config = self._load_config()
        for plugin_id, enabled in saved_config.items():
            if plugin_id in self.plugins:
                self.plugins[plugin_id].enabled = enabled
                print(f"Plugin '{plugin_id}': {'enabled' if enabled else 'disabled'} (from config)")
    
    def enable_plugin(self, plugin_id: str):
        """Enable a plugin and persist the state"""
        if plugin_id in self.plugins:
            self.plugins[plugin_id].enabled = True
            self._save_config()
    
    def disable_plugin(self, plugin_id: str):
        """Disable a plugin and persist the state"""
        if plugin_id in self.plugins:
            self.plugins[plugin_id].enabled = False
            self._save_config()
    
    def run_hook(self, hook_name: str, **kwargs):
        """
        Run a hook on all enabled plugins.
        
        For hooks that can transform content (on_note_save, on_note_load):
        - Pass 'content' in kwargs
        - Returns the transformed content after all plugins process it
        
        For void hooks (on_note_delete, on_search, on_app_startup):
        - Just executes the hooks
        - Returns None
        
        Args:
            hook_name: Name of the hook to run
            **kwargs: Arguments to pass to the hook
            
        Returns:
            Transformed content if 'content' in kwargs, otherwise None
        """
        result = kwargs.get('content')
        
        for plugin in self.plugins.values():
            if plugin.enabled and hasattr(plugin, hook_name):
                try:
                    method = getattr(plugin, hook_name)
                    
                    # For hooks that can transform content
                    if 'content' in kwargs:
                        transformed = method(**{**kwargs, 'content': result})
                        if transformed is not None:
                            result = transformed
                    else:
                        # For void hooks (no return value)
                        method(**kwargs)
                        
                except Exception as e:
                    print(f"Plugin {plugin.name} error in {hook_name}: {e}")
        
        return result if 'content' in kwargs else None
    
    def run_hook_with_return(self, hook_name: str, **kwargs):
        """
        Run a hook that can modify and return a value (e.g., on_note_create).
        Each plugin processes the value from the previous plugin.
        
        The hook method should accept all kwargs and return the modified value.
        For on_note_create: expects (note_path, initial_content) and returns modified content.
        
        Args:
            hook_name: Name of the hook to run
            **kwargs: Arguments to pass to the hook (including the value to modify)
            
        Returns:
            Modified value after all plugins have processed it
        """
        for plugin in self.plugins.values():
            if plugin.enabled and hasattr(plugin, hook_name):
                try:
                    method = getattr(plugin, hook_name)
                    result = method(**kwargs)
                    # Update the modifiable value for the next plugin
                    if 'initial_content' in kwargs and result is not None:
                        kwargs['initial_content'] = result
                except Exception as e:
                    print(f"Plugin {plugin.name} error in {hook_name}: {e}")
        
        # Return the final modified value
        return kwargs.get('initial_content', '')
    

