#!/usr/bin/env python3
"""
Printer Window Detection Module for Workstatus
Detects printer-related windows and dialogs as active applications (isapp=true)
"""

import platform
import time
import threading
import subprocess
import re
from typing import List, Dict, Optional
import psutil

from ws_package.log.writer import LogWriter
from ws_package.configs import config

logger = LogWriter.file(__name__)

PRINTER_DETECTION_CACHE_TTL_SECONDS = 60.0

class PrinterWindowDetector:
    """
    Detects printer-related windows and dialogs as active applications.
    This ensures printer windows are captured as isapp=true in workstatus monitoring.
    """
    
    def __init__(self):
        self.platform = platform.system().lower()
        self.printer_window_keywords = [
            'print', 'printer', 'printing', 'print preview', 'print dialog',
            'print queue', 'print spooler', 'print job', 'print status',
            'cups', 'lp', 'lpr', 'print manager', 'print server',
            'document', 'pdf', 'print to file', 'print settings',
            'printer properties', 'printer preferences', 'printer setup'
        ]
        
        # Platform-specific printer process names
        self.printer_processes = {
            'windows': [
                'spoolsv.exe', 'winspool.drv', 'printui.exe', 'rundll32.exe',
                'mscorsvw.exe', 'dllhost.exe', 'explorer.exe'  # explorer.exe for print dialogs
            ],
            'linux': [
                'cupsd', 'lp', 'lpr', 'lpstat', 'cups-browsed', 'systemd',
                'gnome-control-center', 'kde-print-manager', 'system-config-printer'
            ],
            'darwin': [
                'cupsd', 'lp', 'lpr', 'lpstat', 'cups-browsed', 'Print Center',
                'System Preferences', 'printer-setup', 'cups'
            ]
        }
        
        # logger.info(f"PrinterWindowDetector initialized for {self.platform}")
        self._cache_result: List[Dict] = []
        self._cache_time = 0.0
    
    def detect_printer_windows(self) -> List[Dict]:
        """
        Detect active printer-related windows and return them as active applications.
        Returns list of printer windows that should be treated as isapp=true
        """
        now = time.time()
        if self._cache_result and (now - self._cache_time) < PRINTER_DETECTION_CACHE_TTL_SECONDS:
            return list(self._cache_result)

        printer_windows = []
        
        try:
            if self.platform == "windows":
                printer_windows.extend(self._detect_windows_printer_windows())
            elif self.platform == "linux":
                printer_windows.extend(self._detect_linux_printer_windows())
            elif self.platform == "darwin":
                printer_windows.extend(self._detect_mac_printer_windows())
                
        except Exception as e:
            logger.error("Error detecting printer windows: %s", e)
        
        self._cache_result = printer_windows
        self._cache_time = time.time()
        return printer_windows
    
    def _detect_windows_printer_windows(self) -> List[Dict]:
        """Detect printer windows on Windows"""
        printer_windows = []
        
        try:
            import win32gui
            import win32process
            
            def enum_windows_callback(hwnd, windows):
                if win32gui.IsWindowVisible(hwnd):
                    window_title = win32gui.GetWindowText(hwnd)
                    if window_title and self._is_printer_window(window_title):
                        try:
                            _, pid = win32process.GetWindowThreadProcessId(hwnd)
                            process = psutil.Process(pid)
                            process_name = process.name()
                            
                            printer_windows.append({
                                "timestamp": str(time.strftime('%Y-%m-%d %H:%M:%S')),
                                "module": f"Printer: {window_title}",
                                "active_app": f"Printer: {window_title}",
                                "url": f"Printer: {window_title}"
                            })
                        except Exception as e:
                            logger.debug("Error processing window %s: %s", hwnd, e)
                
                return True
            
            # Enumerate all windows
            win32gui.EnumWindows(enum_windows_callback, printer_windows)
            
        except ImportError:
            logger.warning("win32gui not available - Windows printer window detection disabled")
        except Exception as e:
            logger.error("Error detecting Windows printer windows: %s", e)
        
        return printer_windows
    
    def _detect_linux_printer_windows(self) -> List[Dict]:
        """Detect printer windows on Linux"""
        printer_windows = []
        
        try:
            # Get active window using xprop
            result = subprocess.run([
                'xprop', '-root', '_NET_ACTIVE_WINDOW'
            ], capture_output=True, text=True, timeout=5)
            
            if result.returncode == 0:
                window_id = result.stdout.strip().split()[-1]
                
                # Get window title
                title_result = subprocess.run([
                    'xprop', '-id', window_id, 'WM_NAME'
                ], capture_output=True, text=True, timeout=5)
                
                if title_result.returncode == 0:
                    window_title = title_result.stdout.strip()
                    # Extract title from output like "WM_NAME(STRING) = "Print Document""
                    if '=' in window_title:
                        window_title = window_title.split('=', 1)[1].strip().strip('"')
                        
                        if self._is_printer_window(window_title):
                            # Get process info
                            pid_result = subprocess.run([
                                'xprop', '-id', window_id, '_NET_WM_PID'
                            ], capture_output=True, text=True, timeout=5)
                            
                            pid = None
                            process_name = "Unknown"
                            
                            if pid_result.returncode == 0:
                                pid_line = pid_result.stdout.strip()
                                if '=' in pid_line:
                                    pid = int(pid_line.split('=')[1].strip())
                                    try:
                                        process = psutil.Process(pid)
                                        process_name = process.name()
                                    except Exception:
                                        pass
                            
                            printer_windows.append({
                                "timestamp": str(time.strftime('%Y-%m-%d %H:%M:%S')),
                                "module": f"Printer: {window_title}",
                                "active_app": f"Printer: {window_title}",
                                "url": f"Printer: {window_title}"
                            })
            
            # Also check for CUPS web interface
            printer_windows.extend(self._check_cups_web_interface())
            
        except Exception as e:
            logger.error("Error detecting Linux printer windows: %s", e)
        
        return printer_windows
    
    def _detect_mac_printer_windows(self) -> List[Dict]:
        """Detect printer-related processes on macOS using psutil (no AppleScript).

        The previous implementation used a blocking subprocess('osascript') call that
        ran every 2 seconds (via monitor_active_app_for_ui → get_active_windows →
        enhance_window_detection_with_printers) and blocked the asyncio event loop for
        up to 5 seconds, causing visible tracking gaps. Replaced with a non-blocking
        psutil process scan that looks for active CUPS/print daemons.
        """
        printer_windows = []
        _print_process_names = frozenset({
            'cupsd', 'cups-browsed', 'lp', 'lpr', 'lpstat',
            'printer-setup', 'Print Center',
        })
        try:
            for proc in psutil.process_iter(['name']):
                try:
                    pname = proc.info.get('name') or ''
                    if pname in _print_process_names:
                        printer_windows.append({
                            "timestamp": str(time.strftime('%Y-%m-%d %H:%M:%S')),
                            "module": f"Printer: {pname}",
                            "active_app": f"Printer: {pname}",
                            "url": f"Printer: {pname}",
                        })
                        break  # one entry is enough per cycle
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
        except Exception as e:
            logger.debug("macOS printer process detection error: %s", e)
        return printer_windows
    
    def _check_cups_web_interface(self) -> List[Dict]:
        """Check if CUPS web interface is active"""
        printer_windows = []
        
        try:
            # Check if CUPS web interface is running
            result = subprocess.run([
                'lpstat', '-p'
            ], capture_output=True, text=True, timeout=5)
            
            if result.returncode == 0:
                # Check if any browser is accessing localhost:631 (CUPS web interface)
                for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
                    try:
                        if proc.info['name'] in ['firefox', 'google-chrome', 'chromium', 'brave']:
                            cmdline = ' '.join(proc.info['cmdline'] or [])
                            if 'localhost:631' in cmdline or '127.0.0.1:631' in cmdline:
                                printer_windows.append({
                                    "timestamp": str(time.strftime('%Y-%m-%d %H:%M:%S')),
                                    "module": "CUPS Web Interface",
                                    "active_app": "CUPS Web Interface",
                                    "url": "CUPS Web Interface"
                                })
                                break
                    except (psutil.NoSuchProcess, psutil.AccessDenied):
                        continue
                        
        except Exception as e:
            logger.debug("Error checking CUPS web interface: %s", e)
        
        return printer_windows
    
    def _is_printer_window(self, window_title: str) -> bool:
        """Check if window title indicates printer-related activity"""
        if not window_title:
            return False
        
        title_lower = window_title.lower()
        
        # Check for printer keywords
        for keyword in self.printer_window_keywords:
            if keyword in title_lower:
                return True
        
        # Check for common printer dialog patterns
        printer_patterns = [
            r'print.*dialog',
            r'print.*preview',
            r'print.*settings',
            r'print.*queue',
            r'print.*job',
            r'printer.*properties',
            r'printer.*preferences',
            r'print.*to.*file',
            r'print.*document',
            r'printing.*progress',
            r'print.*status'
        ]
        
        for pattern in printer_patterns:
            if re.search(pattern, title_lower):
                return True
        
        return False
    
    def _is_printer_app(self, app_name: str) -> bool:
        """Check if application name indicates printer-related activity"""
        if not app_name:
            return False
        
        app_lower = app_name.lower()
        
        # Check for printer-related applications
        printer_apps = [
            'print center', 'print manager', 'cups', 'printer setup',
            'system preferences', 'gnome-control-center', 'kde-print-manager',
            'system-config-printer', 'printer utility'
        ]
        
        for app in printer_apps:
            if app in app_lower:
                return True
        
        return False
    
    def get_active_printer_processes(self) -> List[Dict]:
        """Get list of active printer-related processes"""
        printer_processes = []
        
        try:
            current_processes = self.printer_processes.get(self.platform, [])
            
            for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'status']):
                try:
                    proc_name = proc.info['name'].lower()
                    
                    # Check if process is printer-related
                    if any(printer_proc in proc_name for printer_proc in current_processes):
                        printer_processes.append({
                            "pid": proc.info['pid'],
                            "name": proc.info['name'],
                            "status": proc.info['status'],
                            "cmdline": ' '.join(proc.info['cmdline'] or []),
                            "platform": self.platform
                        })
                        
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
                    
        except Exception as e:
            logger.error("Error getting printer processes: %s", e)
        
        return printer_processes
    
    def is_printer_activity_active(self) -> bool:
        """Check if any printer activity is currently active"""
        try:
            # Check for printer windows
            printer_windows = self.detect_printer_windows()
            if printer_windows:
                return True
            
            # Check for printer processes
            printer_processes = self.get_active_printer_processes()
            if printer_processes:
                return True
            
            return False
            
        except Exception as e:
            logger.error("Error checking printer activity: %s", e)
            return False


# Integration function for existing window monitoring
# Module-level singleton — avoids re-creating PrinterWindowDetector (and re-running
# EnumWindows on Windows) on every call to enhance_window_detection_with_printers().
_printer_detector: Optional["PrinterWindowDetector"] = None


def _get_printer_detector() -> "PrinterWindowDetector":
    global _printer_detector
    if _printer_detector is None:
        _printer_detector = PrinterWindowDetector()
    return _printer_detector


def enhance_window_detection_with_printers(
    original_windows: List[Dict],
    *,
    enabled: bool = True,
) -> List[Dict]:
    """
    Enhance existing window detection by adding printer windows.
    This function should be called from your existing window monitoring code.
    """
    try:
        # Handle None case - original_windows might be None if get_active_windows_mac() returns None
        if original_windows is None:
            original_windows = []

        if not enabled:
            return list(original_windows) if original_windows else []

        detector = _get_printer_detector()
        printer_windows = detector.detect_printer_windows()

        # Combine original windows with printer windows
        enhanced_windows = original_windows.copy() if original_windows else []
        enhanced_windows.extend(printer_windows)

        # Log printer windows if found (debug only — this is called every 2–20 s)
        if printer_windows:
            logger.info("Detected %s printer window(s) as active apps", len(printer_windows))
            for window in printer_windows:
                logger.debug("Printer window detected: %s", window.get("active_app", "unknown"))

        # REMOVED: logger.info(enhanced_windows) — was logging the full window list on every
        # call (every 2 s from monitor_active_app_for_ui), causing massive log spam.
        logger.debug("enhance_window_detection_with_printers: %s window(s) total", len(enhanced_windows))
        return enhanced_windows

    except Exception as e:
        logger.error("Error enhancing window detection with printers: %s", e)
        # Return original_windows or empty list if None
        return original_windows if original_windows is not None else []


# Factory function for easy integration
def create_printer_window_detector() -> PrinterWindowDetector:
    """Create printer window detector instance"""
    return PrinterWindowDetector()
