"""
Template file watcher for monitoring HTML template changes
"""

import os
import time
import logging
import platform
from pathlib import Path
from typing import Dict, Any, Optional, List
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

from ..core.thread_manager import ThreadedComponent
from ..core.message_bus import MessageBus, MessageBuilder, MessageType

logger = logging.getLogger(__name__)


class TemplateChangeHandler(FileSystemEventHandler):
    """Handle template file system events"""
    
    def __init__(self, message_bus: MessageBus, component_name: str):
        super().__init__()
        self.message_bus = message_bus
        self.component_name = component_name
        self.templates_cache = {}
        logger.info("TemplateChangeHandler initialized")
    
    def on_modified(self, event):
        """Handle file modification events"""
        if not event.is_directory and event.src_path.endswith('.html'):
            self._handle_template_change(event.src_path, 'modified')
    
    def on_created(self, event):
        """Handle file creation events"""
        if not event.is_directory and event.src_path.endswith('.html'):
            self._handle_template_change(event.src_path, 'created')
    
    def on_deleted(self, event):
        """Handle file deletion events"""
        if not event.is_directory and event.src_path.endswith('.html'):
            self._handle_template_change(event.src_path, 'deleted')
    
    def on_moved(self, event):
        """Handle file move/rename events"""
        if not event.is_directory:
            if event.src_path.endswith('.html'):
                self._handle_template_change(event.src_path, 'deleted')
            if event.dest_path.endswith('.html'):
                self._handle_template_change(event.dest_path, 'created')
    
    def _handle_template_change(self, file_path: str, change_type: str):
        """Handle template file changes"""
        try:
            template_name = Path(file_path).name
            
            # Read template content if file exists
            template_content = None
            if change_type != 'deleted' and os.path.exists(file_path):
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        template_content = f.read()
                except Exception as e:
                    logger.error(f"Failed to read template {file_path}: {e}")
                    return
            
            # Prepare template data
            template_data = {
                'template_name': template_name,
                'file_path': file_path,
                'change_type': change_type,
                'timestamp': time.time()
            }
            
            if template_content:
                template_data['content'] = template_content
            
            # Send TEMPLATE_CHANGE message
            message = MessageBuilder.template_change(
                sender=self.component_name,
                template_name=template_name,
                template_data=template_data
            )
            
            self.message_bus.publish(message, broadcast=True)
            logger.info(f"Template {change_type}: {template_name}")
            
        except Exception as e:
            logger.error(f"Failed to handle template change: {e}")


class TemplateWatcher(ThreadedComponent):
    """File system watcher for HTML templates"""
    
    def __init__(self, message_bus: MessageBus, templates_dir: str, uploaded_templates_dir: str = None):
        super().__init__("template_watcher", message_bus)
        self.builtin_templates_dir = Path(templates_dir)
        self.uploaded_templates_dir = Path(uploaded_templates_dir) if uploaded_templates_dir else self._get_persistent_templates_dir()
        self.observer: Optional[Observer] = None
        self.event_handlers: List[TemplateChangeHandler] = []
        
        # Ensure directories exist
        self.builtin_templates_dir.mkdir(parents=True, exist_ok=True)
        self.uploaded_templates_dir.mkdir(parents=True, exist_ok=True)
        
        # Backwards compatibility
        self.templates_dir = self.builtin_templates_dir
        
        # Register message queue
        self.message_queue = self.message_bus.register_component(self.name)
        
        logger.info(f"TemplateWatcher initialized - builtin: {self.builtin_templates_dir}, uploaded: {self.uploaded_templates_dir}")
    
    def initialize(self) -> bool:
        """Initialize the file watcher for both directories"""
        try:
            # Create observer
            self.observer = Observer()
            
            # Create event handler for built-in templates
            builtin_handler = TemplateChangeHandler(self.message_bus, self.name)
            self.event_handlers.append(builtin_handler)
            self.observer.schedule(
                builtin_handler,
                str(self.builtin_templates_dir),
                recursive=False
            )
            
            # Create event handler for uploaded templates
            uploaded_handler = TemplateChangeHandler(self.message_bus, self.name)
            self.event_handlers.append(uploaded_handler)
            self.observer.schedule(
                uploaded_handler,
                str(self.uploaded_templates_dir),
                recursive=False
            )
            
            logger.info("TemplateWatcher initialized successfully for both directories")
            return True
            
        except Exception as e:
            logger.error(f"TemplateWatcher initialization failed: {e}")
            return False
    
    def run(self):
        """Main run loop"""
        try:
            logger.info("TemplateWatcher thread started")
            
            # Start the file system observer
            if self.observer:
                self.observer.start()
                logger.info(f"Started watching builtin templates: {self.builtin_templates_dir}")
                logger.info(f"Started watching uploaded templates: {self.uploaded_templates_dir}")
            
            # Send ready status
            ready_message = MessageBuilder.system_status(
                sender=self.name,
                status="ready",
                details={
                    "builtin_templates_dir": str(self.builtin_templates_dir),
                    "uploaded_templates_dir": str(self.uploaded_templates_dir)
                }
            )
            self.message_bus.publish(ready_message)
            
            # Scan existing templates on startup from both directories
            self._scan_existing_templates()
            
            # Message processing loop
            while self.running and not self.shutdown_event.is_set():
                try:
                    # Process messages
                    message = self.message_bus.get_message(self.name, timeout=1.0)
                    if message:
                        self._process_message(message)

                    # Update heartbeat
                    self.heartbeat()

                    time.sleep(0.1)

                except Exception as e:
                    logger.error(f"TemplateWatcher run loop error: {e}")
                    time.sleep(1.0)
                    
        except Exception as e:
            logger.error(f"TemplateWatcher run failed: {e}")
        finally:
            # Stop observer
            if self.observer:
                self.observer.stop()
                self.observer.join()
            
            logger.info("TemplateWatcher thread ended")
    
    def shutdown(self):
        """Shutdown template watcher"""
        try:
            logger.info("Shutting down TemplateWatcher...")

            if self.observer:
                # Unschedule all handlers
                for handler in self.event_handlers:
                    try:
                        self.observer.unschedule(handler)
                    except Exception:
                        pass
                self.event_handlers.clear()

                self.observer.stop()
                try:
                    self.observer.join(timeout=2.0)
                except Exception:
                    pass
                self.observer = None

        except Exception as e:
            logger.error(f"TemplateWatcher shutdown error: {e}")
    
    def _process_message(self, message):
        """Process received message"""
        try:
            # Currently no specific messages to handle
            # Future: Could handle requests to reload templates, change watch directory, etc.
            pass
        except Exception as e:
            logger.error(f"Failed to process message: {e}")
    
    def _scan_existing_templates(self):
        """Scan and report existing templates on startup from both directories"""
        try:
            # Scan built-in templates
            if self.builtin_templates_dir.exists():
                builtin_files = list(self.builtin_templates_dir.glob("*.html"))
                logger.info(f"Found {len(builtin_files)} built-in template(s)")
                self._scan_template_directory(builtin_files, "builtin")
            
            # Scan uploaded templates
            if self.uploaded_templates_dir.exists():
                uploaded_files = list(self.uploaded_templates_dir.glob("*.html"))
                logger.info(f"Found {len(uploaded_files)} uploaded template(s)")
                self._scan_template_directory(uploaded_files, "uploaded")
            
        except Exception as e:
            logger.error(f"Failed to scan existing templates: {e}")
    
    def _scan_template_directory(self, html_files: List[Path], source_type: str):
        """Scan templates in a specific directory"""
        for template_file in html_files:
            try:
                # Read template content
                with open(template_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                # Send template change message for existing templates
                template_data = {
                    'template_name': template_file.name,
                    'file_path': str(template_file),
                    'change_type': 'existing',
                    'source_type': source_type,
                    'content': content,
                    'timestamp': time.time()
                }
                
                message = MessageBuilder.template_change(
                    sender=self.name,
                    template_name=template_file.name,
                    template_data=template_data
                )
                
                self.message_bus.publish(message, broadcast=True)
                logger.debug(f"Reported existing {source_type} template: {template_file.name}")
                
            except Exception as e:
                logger.error(f"Failed to process existing template {template_file}: {e}")
    
    def get_available_templates(self) -> List[str]:
        """Get list of available template names from both directories"""
        try:
            templates = set()
            
            # Get built-in templates
            if self.builtin_templates_dir.exists():
                builtin_templates = [f.name for f in self.builtin_templates_dir.glob("*.html")]
                templates.update(builtin_templates)
            
            # Get uploaded templates (these override built-in ones with same name)
            if self.uploaded_templates_dir.exists():
                uploaded_templates = [f.name for f in self.uploaded_templates_dir.glob("*.html")]
                templates.update(uploaded_templates)
            
            return sorted(list(templates))
            
        except Exception as e:
            logger.error(f"Failed to get available templates: {e}")
            return []
    
    def get_template_content(self, template_name: str) -> Optional[str]:
        """Get content of a specific template, prioritizing uploaded templates"""
        try:
            # First try uploaded templates (user uploads take priority)
            template_path = self.uploaded_templates_dir / template_name
            
            # If not found in uploaded, try built-in templates
            if not template_path.exists():
                template_path = self.builtin_templates_dir / template_name
            
            if template_path.exists() and template_path.suffix == '.html':
                with open(template_path, 'r', encoding='utf-8') as f:
                    return f.read()
            else:
                logger.warning(f"Template not found: {template_name}")
                return None
                
        except Exception as e:
            logger.error(f"Failed to get template content for {template_name}: {e}")
            return None
    
    def _get_persistent_templates_dir(self) -> Path:
        """Get persistent templates directory for user uploads"""
        try:
            system = platform.system()
            
            if system == "Windows":
                # Use AppData/Roaming on Windows
                app_data = os.getenv('APPDATA', os.path.expanduser('~'))
                templates_dir = Path(app_data) / "MbetterClient" / "templates"
            elif system == "Darwin":  # macOS
                # Use ~/Library/Application Support on macOS
                templates_dir = Path.home() / "Library" / "Application Support" / "MbetterClient" / "templates"
            else:  # Linux and other Unix-like systems
                # Use ~/.config on Linux
                config_home = os.getenv('XDG_CONFIG_HOME', str(Path.home() / ".config"))
                templates_dir = Path(config_home) / "MbetterClient" / "templates"
            
            logger.debug(f"Persistent templates directory: {templates_dir}")
            return templates_dir
                
        except Exception as e:
            logger.error(f"Failed to determine persistent templates directory: {e}")
            # Fallback to local directory
            return Path.cwd() / "user_templates"
    