"""
Progress Tracking Module

This module provides comprehensive progress tracking with visual indicators (emojis)
for file, module, and submodule processing across console, log files, and Jupyter notebooks.

Key Features:
    - Automatic module/submodule detection via introspection
    - Real-time progress updates with emojis
    - Console, Jupyter notebook, and log file support
    - Zero configuration required - works automatically
    - Final summary display

Main Classes:
    - ProgressTracker: Main tracking coordinator
    - ProgressDisplay: Abstract base class for displays
    - ConsoleProgressDisplay: Real-time console output
    - JupyterProgressDisplay: IPython/Jupyter notebook display
    - FileProgressDisplay: Log file progress tracking
    - track_progress: Decorator for automatic progress tracking

Example Usage:
    >>> from semantica.utils import track_progress
    >>> 
    >>> @track_progress
    >>> def process_file(file_path):
    ...     # Processing code - progress tracked automatically
    ...     pass

Author: Semantica Contributors
License: MIT
"""

import sys
import inspect
import threading
import time
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Dict, List, Optional, Callable, Union, Tuple
from pathlib import Path
from contextlib import contextmanager

from .logging import get_logger

# Try to import IPython for Jupyter support
try:
    from IPython.display import display, clear_output, HTML
    from IPython import get_ipython
    IPYTHON_AVAILABLE = True
except ImportError:
    IPYTHON_AVAILABLE = False


@dataclass
class ProgressItem:
    """Progress tracking item."""
    file: Optional[str] = None
    module: Optional[str] = None
    submodule: Optional[str] = None
    status: str = "pending"  # pending, running, completed, failed
    start_time: Optional[float] = None
    end_time: Optional[float] = None
    message: str = ""
    emoji: str = "⏳"
    metadata: Dict[str, Any] = field(default_factory=dict)


class ProgressDisplay(ABC):
    """Abstract base class for progress displays."""
    
    @abstractmethod
    def update(self, item: ProgressItem) -> None:
        """Update progress display."""
        pass
    
    @abstractmethod
    def show_summary(self, items: List[ProgressItem]) -> None:
        """Show final summary."""
        pass
    
    @abstractmethod
    def clear(self) -> None:
        """Clear display."""
        pass


class ConsoleProgressDisplay(ProgressDisplay):
    """Console progress display with real-time updates."""
    
    def __init__(self, use_emoji: bool = True, update_interval: float = 0.1):
        self.use_emoji = use_emoji
        self.update_interval = update_interval
        self.last_update = 0.0
        self.current_lines: Dict[str, str] = {}
        self.lock = threading.Lock()
    
    def _should_update(self) -> bool:
        """Check if enough time has passed for update."""
        now = time.time()
        if now - self.last_update >= self.update_interval:
            self.last_update = now
            return True
        return False
    
    def _get_emoji_for_module(self, module: str) -> str:
        """Get emoji for module type."""
        if not self.use_emoji:
            return ""
        
        emoji_map = {
            "ingest": "📥",
            "parse": "🔍",
            "kg": "🧠",
            "embeddings": "💾",
            "normalize": "🔧",
            "ontology": "📚",
            "semantic_extract": "🎯",
            "seed": "🌱",
            "split": "✂️",
            "triple_store": "🗄️",
            "vector_store": "📊",
            "export": "💾",
            "reasoning": "🤔",
            "kg_qa": "✅",
            "visualization": "📈",
            "context": "🔗",
            "conflicts": "⚠️",
            "deduplication": "🔄",
            "pipeline": "⚙️",
        }
        
        # Check if module name contains any key
        module_lower = module.lower()
        for key, emoji in emoji_map.items():
            if key in module_lower:
                return emoji
        
        return "⏳"
    
    def _get_status_emoji(self, status: str) -> str:
        """Get emoji for status."""
        if not self.use_emoji:
            return ""
        
        status_map = {
            "pending": "⏳",
            "running": "🔄",
            "completed": "✅",
            "failed": "❌",
        }
        return status_map.get(status, "⏳")
    
    def _get_action_message(self, module: Optional[str], message: str) -> str:
        """Get action message based on module."""
        # Extract action from message if it contains "Semantica:"
        if message and "Semantica:" in message:
            # Use the message as-is if it already has Semantica format
            return message.split("Semantica:")[-1].strip() if "Semantica:" in message else message
        
        # Map modules to actions
        action_map = {
            "ingest": "is ingesting",
            "parse": "is parsing",
            "kg": "is building",
            "embeddings": "is embedding",
            "normalize": "is normalizing",
            "ontology": "is generating",
            "semantic_extract": "is extracting",
            "seed": "is seeding",
            "split": "is splitting",
            "triple_store": "is storing",
            "vector_store": "is indexing",
            "export": "is exporting",
            "reasoning": "is reasoning",
            "kg_qa": "is validating",
            "visualization": "is visualizing",
            "context": "is processing",
            "conflicts": "is resolving",
            "deduplication": "is deduplicating",
            "pipeline": "is executing",
            "core": "is processing",
        }
        
        action = action_map.get(module or "", "is processing")
        return f"Semantica {action}"
    
    def update(self, item: ProgressItem) -> None:
        """Update console progress display."""
        if not self._should_update():
            return
        
        with self.lock:
            key = f"{item.module}:{item.submodule}"
            if item.file:
                key = f"{item.file}:{key}"
            
            # Build progress line
            parts = []
            
            # Semantica branding with action
            if self.use_emoji:
                parts.append("🧠")
            
            # Create action message based on module
            action_msg = self._get_action_message(item.module, item.message)
            parts.append(action_msg)
            
            # Status emoji
            if self.use_emoji:
                parts.append(self._get_status_emoji(item.status))
            
            # Module emoji
            if item.module and self.use_emoji:
                parts.append(self._get_emoji_for_module(item.module))
            
            # Module name
            if item.module:
                parts.append(f"[{item.module}]")
            
            # Submodule
            if item.submodule:
                parts.append(f"{item.submodule}")
            
            # File
            if item.file:
                file_name = Path(item.file).name if item.file else ""
                parts.append(f"📄 {file_name}")
            
            # Time elapsed
            if item.start_time:
                elapsed = time.time() - item.start_time
                parts.append(f"({elapsed:.1f}s)")
            
            line = " ".join(parts)
            self.current_lines[key] = line
            
            # Print all current lines (overwrite previous)
            sys.stdout.write("\r" + " " * 100 + "\r")  # Clear line
            if len(self.current_lines) == 1:
                sys.stdout.write(line)
            else:
                # Multiple items - show all
                lines_list = list(self.current_lines.values())
                sys.stdout.write("\n".join(lines_list[-3:]))  # Show last 3
            sys.stdout.flush()
    
    def show_summary(self, items: List[ProgressItem]) -> None:
        """Show final summary."""
        with self.lock:
            # Clear current display
            sys.stdout.write("\n" + "=" * 80 + "\n")
            
            # Summary header
            if self.use_emoji:
                sys.stdout.write("🧠 Semantica - 📊 Progress Summary\n")
            else:
                sys.stdout.write("Semantica - Progress Summary\n")
            sys.stdout.write("=" * 80 + "\n")
            
            # Group by module
            by_module: Dict[str, List[ProgressItem]] = {}
            for item in items:
                module = item.module or "unknown"
                if module not in by_module:
                    by_module[module] = []
                by_module[module].append(item)
            
            # Show summary by module
            total_time = 0.0
            completed = 0
            failed = 0
            
            for module, module_items in by_module.items():
                if self.use_emoji:
                    emoji = self._get_emoji_for_module(module)
                    sys.stdout.write(f"\n{emoji} {module.upper()}\n")
                else:
                    sys.stdout.write(f"\n{module.upper()}\n")
                sys.stdout.write("-" * 80 + "\n")
                
                for item in module_items:
                    status_emoji = self._get_status_emoji(item.status)
                    duration = ""
                    if item.start_time and item.end_time:
                        duration = f" ({item.end_time - item.start_time:.2f}s)"
                    elif item.start_time:
                        duration = f" (running...)"
                    
                    line = f"  {status_emoji} {item.submodule or 'N/A'}"
                    if item.file:
                        line += f" - {Path(item.file).name}"
                    line += duration
                    sys.stdout.write(line + "\n")
                    
                    if item.status == "completed":
                        completed += 1
                    elif item.status == "failed":
                        failed += 1
                    
                    if item.start_time and item.end_time:
                        total_time += (item.end_time - item.start_time)
            
            # Final stats
            sys.stdout.write("\n" + "=" * 80 + "\n")
            if self.use_emoji:
                sys.stdout.write(f"✅ Completed: {completed} | ❌ Failed: {failed} | ⏱️  Total Time: {total_time:.2f}s\n")
            else:
                sys.stdout.write(f"Completed: {completed} | Failed: {failed} | Total Time: {total_time:.2f}s\n")
            sys.stdout.write("=" * 80 + "\n")
            sys.stdout.flush()
    
    def clear(self) -> None:
        """Clear console display."""
        with self.lock:
            sys.stdout.write("\r" + " " * 100 + "\r")
            sys.stdout.flush()
            self.current_lines.clear()


class JupyterProgressDisplay(ProgressDisplay):
    """Jupyter notebook progress display."""
    
    def __init__(self, use_emoji: bool = True):
        self.use_emoji = use_emoji
        self.display_handle = None
        self.items: List[ProgressItem] = []
    
    def _get_emoji_for_module(self, module: str) -> str:
        """Get emoji for module type."""
        if not self.use_emoji:
            return ""
        
        emoji_map = {
            "ingest": "📥",
            "parse": "🔍",
            "kg": "🧠",
            "embeddings": "💾",
            "normalize": "🔧",
            "ontology": "📚",
            "semantic_extract": "🎯",
            "seed": "🌱",
            "split": "✂️",
            "triple_store": "🗄️",
            "vector_store": "📊",
            "export": "💾",
            "reasoning": "🤔",
            "kg_qa": "✅",
            "visualization": "📈",
            "context": "🔗",
            "conflicts": "⚠️",
            "deduplication": "🔄",
            "pipeline": "⚙️",
        }
        
        module_lower = module.lower()
        for key, emoji in emoji_map.items():
            if key in module_lower:
                return emoji
        return "⏳"
    
    def _get_status_emoji(self, status: str) -> str:
        """Get emoji for status."""
        if not self.use_emoji:
            return ""
        
        status_map = {
            "pending": "⏳",
            "running": "🔄",
            "completed": "✅",
            "failed": "❌",
        }
        return status_map.get(status, "⏳")
    
    def _get_action_message(self, module: Optional[str], message: str) -> str:
        """Get action message based on module."""
        # Extract action from message if it contains "Semantica:"
        if message and "Semantica:" in message:
            # Use the message as-is if it already has Semantica format
            return message.split("Semantica:")[-1].strip() if "Semantica:" in message else message
        
        # Map modules to actions
        action_map = {
            "ingest": "is ingesting",
            "parse": "is parsing",
            "kg": "is building",
            "embeddings": "is embedding",
            "normalize": "is normalizing",
            "ontology": "is generating",
            "semantic_extract": "is extracting",
            "seed": "is seeding",
            "split": "is splitting",
            "triple_store": "is storing",
            "vector_store": "is indexing",
            "export": "is exporting",
            "reasoning": "is reasoning",
            "kg_qa": "is validating",
            "visualization": "is visualizing",
            "context": "is processing",
            "conflicts": "is resolving",
            "deduplication": "is deduplicating",
            "pipeline": "is executing",
            "core": "is processing",
        }
        
        action = action_map.get(module or "", "is processing")
        return f"Semantica {action}"
    
    def _build_html(self, items: List[ProgressItem]) -> str:
        """Build HTML for display."""
        html_parts = ["<div style='font-family: monospace;'>"]
        
        # Current status
        html_parts.append("<h4>🧠 Semantica - 📊 Current Progress</h4>")
        html_parts.append("<table style='width: 100%; border-collapse: collapse;'>")
        html_parts.append("<tr><th>Status</th><th>Action</th><th>Module</th><th>Submodule</th><th>File</th><th>Time</th></tr>")
        
        # Show last 10 items
        for item in items[-10:]:
            status_emoji = self._get_status_emoji(item.status)
            module_emoji = self._get_emoji_for_module(item.module or "")
            action_msg = self._get_action_message(item.module, item.message)
            
            elapsed = ""
            if item.start_time:
                if item.end_time:
                    elapsed = f"{(item.end_time - item.start_time):.2f}s"
                else:
                    elapsed = f"{(time.time() - item.start_time):.2f}s"
            
            file_name = Path(item.file).name if item.file else "-"
            
            html_parts.append(
                f"<tr>"
                f"<td>{status_emoji}</td>"
                f"<td>{action_msg}</td>"
                f"<td>{module_emoji} {item.module or 'N/A'}</td>"
                f"<td>{item.submodule or 'N/A'}</td>"
                f"<td>{file_name}</td>"
                f"<td>{elapsed}</td>"
                f"</tr>"
            )
        
        html_parts.append("</table>")
        html_parts.append("</div>")
        
        return "".join(html_parts)
    
    def update(self, item: ProgressItem) -> None:
        """Update Jupyter progress display."""
        # Add or update item
        existing = None
        for i, existing_item in enumerate(self.items):
            if (existing_item.module == item.module and 
                existing_item.submodule == item.submodule and
                existing_item.file == item.file):
                existing = i
                break
        
        if existing is not None:
            self.items[existing] = item
        else:
            self.items.append(item)
        
        # Update display
        if IPYTHON_AVAILABLE:
            html = self._build_html(self.items)
            if self.display_handle is None:
                self.display_handle = display(HTML(html), display_id=True)
            else:
                self.display_handle.update(HTML(html))
    
    def show_summary(self, items: List[ProgressItem]) -> None:
        """Show final summary in Jupyter."""
        if not IPYTHON_AVAILABLE:
            return
        
        html_parts = ["<div style='font-family: monospace;'>"]
        html_parts.append("<h3>🧠 Semantica - 📊 Progress Summary</h3>")
        
        # Group by module
        by_module: Dict[str, List[ProgressItem]] = {}
        for item in items:
            module = item.module or "unknown"
            if module not in by_module:
                by_module[module] = []
            by_module[module].append(item)
        
        # Summary table
        html_parts.append("<table style='width: 100%; border-collapse: collapse; border: 1px solid #ddd;'>")
        html_parts.append(
            "<tr style='background-color: #f2f2f2;'>"
            "<th>Module</th><th>Submodule</th><th>File</th><th>Status</th><th>Time</th>"
            "</tr>"
        )
        
        completed = 0
        failed = 0
        total_time = 0.0
        
        for module, module_items in by_module.items():
            module_emoji = self._get_emoji_for_module(module)
            for item in module_items:
                status_emoji = self._get_status_emoji(item.status)
                duration = ""
                if item.start_time and item.end_time:
                    duration = f"{(item.end_time - item.start_time):.2f}s"
                    total_time += (item.end_time - item.start_time)
                elif item.start_time:
                    duration = "running..."
                
                file_name = Path(item.file).name if item.file else "-"
                
                html_parts.append(
                    f"<tr>"
                    f"<td>{module_emoji} {module}</td>"
                    f"<td>{item.submodule or 'N/A'}</td>"
                    f"<td>{file_name}</td>"
                    f"<td>{status_emoji}</td>"
                    f"<td>{duration}</td>"
                    f"</tr>"
                )
                
                if item.status == "completed":
                    completed += 1
                elif item.status == "failed":
                    failed += 1
        
        html_parts.append("</table>")
        
        # Stats
        html_parts.append(f"<p><strong>✅ Completed:</strong> {completed} | <strong>❌ Failed:</strong> {failed} | <strong>⏱️ Total Time:</strong> {total_time:.2f}s</p>")
        html_parts.append("</div>")
        
        html = "".join(html_parts)
        display(HTML(html))
    
    def clear(self) -> None:
        """Clear Jupyter display."""
        if IPYTHON_AVAILABLE and self.display_handle:
            clear_output(wait=True)
            self.display_handle = None
        self.items.clear()


class FileProgressDisplay(ProgressDisplay):
    """File-based progress display (logs)."""
    
    def __init__(self, logger=None):
        self.logger = logger or get_logger("progress")
        self.items: List[ProgressItem] = []
    
    def update(self, item: ProgressItem) -> None:
        """Update file progress display."""
        # Add or update item
        existing = None
        for i, existing_item in enumerate(self.items):
            if (existing_item.module == item.module and 
                existing_item.submodule == item.submodule and
                existing_item.file == item.file):
                existing = i
                break
        
        if existing is not None:
            self.items[existing] = item
        else:
            self.items.append(item)
        
        # Log progress
        parts = [f"[{item.status.upper()}]"]
        if item.module:
            parts.append(f"Module: {item.module}")
        if item.submodule:
            parts.append(f"Submodule: {item.submodule}")
        if item.file:
            parts.append(f"File: {Path(item.file).name}")
        if item.message:
            parts.append(f"Message: {item.message}")
        
        self.logger.info(" | ".join(parts))
    
    def show_summary(self, items: List[ProgressItem]) -> None:
        """Show final summary in log file."""
        self.logger.info("=" * 80)
        self.logger.info("SEMANTICA - PROGRESS SUMMARY")
        self.logger.info("=" * 80)
        
        # Group by module
        by_module: Dict[str, List[ProgressItem]] = {}
        for item in items:
            module = item.module or "unknown"
            if module not in by_module:
                by_module[module] = []
            by_module[module].append(item)
        
        completed = 0
        failed = 0
        total_time = 0.0
        
        for module, module_items in by_module.items():
            self.logger.info(f"\n{module.upper()}:")
            for item in module_items:
                duration = ""
                if item.start_time and item.end_time:
                    duration = f" ({(item.end_time - item.start_time):.2f}s)"
                    total_time += (item.end_time - item.start_time)
                
                file_name = Path(item.file).name if item.file else "N/A"
                self.logger.info(
                    f"  [{item.status.upper()}] {item.submodule or 'N/A'} - {file_name}{duration}"
                )
                
                if item.status == "completed":
                    completed += 1
                elif item.status == "failed":
                    failed += 1
        
        self.logger.info("=" * 80)
        self.logger.info(f"Completed: {completed} | Failed: {failed} | Total Time: {total_time:.2f}s")
        self.logger.info("=" * 80)
    
    def clear(self) -> None:
        """Clear file display (no-op for logs)."""
        pass


class ModuleDetector:
    """Utility for automatic module/submodule detection."""
    
    @staticmethod
    def detect_from_frame(frame) -> Tuple[Optional[str], Optional[str]]:
        """
        Detect module and submodule from frame.
        
        Returns:
            Tuple of (module_name, submodule_name)
        """
        try:
            # Get the frame's module
            module_name = frame.f_globals.get('__name__', '')
            
            # Extract module name (e.g., 'semantica.ingest.file_ingestor' -> 'ingest')
            if 'semantica.' in module_name:
                parts = module_name.split('.')
                if len(parts) >= 2:
                    module_name = parts[1]  # Get 'ingest', 'parse', etc.
                else:
                    module_name = None
            else:
                module_name = None
            
            # Get class name from 'self' if available
            submodule_name = None
            if 'self' in frame.f_locals:
                self_obj = frame.f_locals['self']
                if hasattr(self_obj, '__class__'):
                    submodule_name = self_obj.__class__.__name__
            elif 'cls' in frame.f_locals:
                cls_obj = frame.f_locals['cls']
                if isinstance(cls_obj, type):
                    submodule_name = cls_obj.__name__
            
            # Fallback: try to get from frame code
            if not submodule_name:
                code = frame.f_code
                # Try to extract class name from qualified name
                if hasattr(code, 'co_qualname'):
                    qualname = code.co_qualname
                    if '.' in qualname:
                        submodule_name = qualname.split('.')[0]
            
            return module_name, submodule_name
            
        except Exception:
            return None, None
    
    @staticmethod
    def detect_from_call_stack(depth: int = 2) -> Tuple[Optional[str], Optional[str]]:
        """
        Detect module and submodule from call stack.
        
        Args:
            depth: Stack depth to check (default: 2, meaning caller's caller)
        
        Returns:
            Tuple of (module_name, submodule_name)
        """
        try:
            frame = inspect.currentframe()
            if frame is None:
                return None, None
            
            # Go up the stack to find the actual caller
            for _ in range(depth):
                frame = frame.f_back
                if frame is None:
                    return None, None
            
            return ModuleDetector.detect_from_frame(frame)
        except Exception:
            return None, None


class ProgressTracker:
    """Main progress tracking coordinator."""
    
    _instance: Optional['ProgressTracker'] = None
    _lock = threading.Lock()
    
    def __init__(self, enabled: bool = True, use_emoji: bool = True, 
                 update_interval: float = 0.1):
        """
        Initialize progress tracker.
        
        Args:
            enabled: Enable progress tracking
            use_emoji: Use emoji indicators
            update_interval: Minimum time between updates (seconds)
        """
        self.enabled = enabled
        self.use_emoji = use_emoji
        self.update_interval = update_interval
        
        # Detect environment
        self.is_jupyter = self._detect_jupyter()
        
        # Create displays
        self.displays: List[ProgressDisplay] = []
        
        if self.is_jupyter and IPYTHON_AVAILABLE:
            self.displays.append(JupyterProgressDisplay(use_emoji=use_emoji))
        else:
            self.displays.append(ConsoleProgressDisplay(use_emoji=use_emoji, 
                                                         update_interval=update_interval))
        
        # Always add file display
        self.displays.append(FileProgressDisplay())
        
        # Track items
        self.items: List[ProgressItem] = []
        self.active_items: Dict[str, ProgressItem] = {}
        self.lock = threading.Lock()
    
    def _detect_jupyter(self) -> bool:
        """Detect if running in Jupyter notebook."""
        if not IPYTHON_AVAILABLE:
            return False
        try:
            ipython = get_ipython()
            return ipython is not None and hasattr(ipython, 'kernel')
        except Exception:
            return False
    
    @classmethod
    def get_instance(cls) -> 'ProgressTracker':
        """Get singleton instance."""
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = cls()
        return cls._instance
    
    def start_tracking(self, file: Optional[str] = None, 
                      module: Optional[str] = None,
                      submodule: Optional[str] = None,
                      message: str = "") -> str:
        """
        Start tracking a progress item.
        
        Args:
            file: File being processed
            module: Module name
            submodule: Submodule/class name
            message: Progress message
        
        Returns:
            Tracking ID
        """
        if not self.enabled:
            return ""
        
        # Auto-detect if not provided
        if not module or not submodule:
            detected_module, detected_submodule = ModuleDetector.detect_from_call_stack(depth=3)
            module = module or detected_module
            submodule = submodule or detected_submodule
        
        # Create tracking ID
        tracking_id = f"{module}:{submodule}:{file or ''}"
        
        with self.lock:
            item = ProgressItem(
                file=file,
                module=module,
                submodule=submodule,
                status="running",
                start_time=time.time(),
                message=message,
                emoji=self._get_emoji_for_module(module or "")
            )
            
            self.active_items[tracking_id] = item
            
            # Update displays
            for display in self.displays:
                display.update(item)
        
        return tracking_id
    
    def update_tracking(self, tracking_id: str, status: str = "running",
                       message: str = "") -> None:
        """
        Update tracking status.
        
        Args:
            tracking_id: Tracking ID from start_tracking
            status: New status (running, completed, failed)
            message: Progress message
        """
        if not self.enabled or not tracking_id:
            return
        
        with self.lock:
            if tracking_id in self.active_items:
                item = self.active_items[tracking_id]
                item.status = status
                item.message = message
                
                if status in ("completed", "failed"):
                    item.end_time = time.time()
                    # Move to completed items
                    self.items.append(item)
                    del self.active_items[tracking_id]
                
                # Update displays
                for display in self.displays:
                    display.update(item)
    
    def stop_tracking(self, tracking_id: str, status: str = "completed",
                     message: str = "") -> None:
        """
        Stop tracking an item.
        
        Args:
            tracking_id: Tracking ID from start_tracking
            status: Final status (completed, failed)
            message: Final message
        """
        self.update_tracking(tracking_id, status=status, message=message)
    
    def _get_emoji_for_module(self, module: str) -> str:
        """Get emoji for module."""
        if not self.use_emoji or not module:
            return ""
        
        emoji_map = {
            "ingest": "📥",
            "parse": "🔍",
            "kg": "🧠",
            "embeddings": "💾",
            "normalize": "🔧",
            "ontology": "📚",
            "semantic_extract": "🎯",
            "seed": "🌱",
            "split": "✂️",
            "triple_store": "🗄️",
            "vector_store": "📊",
            "export": "💾",
            "reasoning": "🤔",
            "kg_qa": "✅",
            "visualization": "📈",
            "context": "🔗",
            "conflicts": "⚠️",
            "deduplication": "🔄",
            "pipeline": "⚙️",
        }
        
        module_lower = module.lower()
        for key, emoji in emoji_map.items():
            if key in module_lower:
                return emoji
        return "⏳"
    
    def show_summary(self) -> None:
        """Show final progress summary."""
        if not self.enabled:
            return
        
        with self.lock:
            # Add any remaining active items
            all_items = self.items + list(self.active_items.values())
            
            # Show summary on all displays
            for display in self.displays:
                display.show_summary(all_items)
    
    @contextmanager
    def track(self, file: Optional[str] = None, 
              module: Optional[str] = None,
              submodule: Optional[str] = None,
              message: str = ""):
        """
        Context manager for automatic tracking.
        
        Usage:
            with progress_tracker.track(file="doc.pdf", message="Processing"):
                # code here
        """
        tracking_id = self.start_tracking(file=file, module=module, 
                                         submodule=submodule, message=message)
        try:
            yield tracking_id
            self.stop_tracking(tracking_id, status="completed")
        except Exception as e:
            self.stop_tracking(tracking_id, status="failed", message=str(e))
            raise


# Global singleton instance
_global_tracker: Optional[ProgressTracker] = None


def get_progress_tracker() -> ProgressTracker:
    """Get global progress tracker instance."""
    global _global_tracker
    if _global_tracker is None:
        _global_tracker = ProgressTracker.get_instance()
    return _global_tracker


def track_progress(file: Optional[str] = None, 
                   module: Optional[str] = None,
                   submodule: Optional[str] = None):
    """
    Decorator for automatic progress tracking.
    
    Usage:
        @track_progress
        def my_function():
            # Automatically tracked
            pass
        
        @track_progress(file="doc.pdf")
        def process_file():
            # Tracked with file context
            pass
    """
    def decorator(func: Callable) -> Callable:
        def wrapper(*args, **kwargs):
            tracker = get_progress_tracker()
            
            # Try to extract file from args/kwargs
            detected_file = file
            if not detected_file:
                # Look for common file parameter names
                for arg_name in ['file', 'file_path', 'path', 'source', 'filename']:
                    if arg_name in kwargs:
                        detected_file = str(kwargs[arg_name])
                        break
                    elif args and isinstance(args[0], (str, Path)):
                        detected_file = str(args[0])
                        break
            
            # Auto-detect module/submodule if not provided
            detected_module = module
            detected_submodule = submodule
            if not detected_module or not detected_submodule:
                frame_module, frame_submodule = ModuleDetector.detect_from_call_stack(depth=2)
                detected_module = detected_module or frame_module
                detected_submodule = detected_submodule or frame_submodule
            
            # Use function name as fallback for submodule
            if not detected_submodule:
                detected_submodule = func.__name__
            
            # Start tracking
            tracking_id = tracker.start_tracking(
                file=detected_file,
                module=detected_module,
                submodule=detected_submodule,
                message=f"Running {func.__name__}"
            )
            
            try:
                result = func(*args, **kwargs)
                tracker.stop_tracking(tracking_id, status="completed", 
                                    message=f"Completed {func.__name__}")
                return result
            except Exception as e:
                tracker.stop_tracking(tracking_id, status="failed", 
                                    message=f"Failed {func.__name__}: {str(e)}")
                raise
        
        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        return wrapper
    
    # Handle both @track_progress and @track_progress(...) usage
    if callable(file):
        # Used as @track_progress without parentheses
        func = file
        file = None
        return decorator(func)
    else:
        # Used as @track_progress(...) with arguments
        return decorator

