"""
Thread-Based Logging System
Provides separate log files for each thread with dedicated logging thread
"""

import logging
import threading
import queue
import time
import json
import os
import sys
from pathlib import Path
from datetime import datetime
from typing import Dict, Optional, Any
from logging.handlers import RotatingFileHandler
import unicodedata

logger = logging.getLogger(__name__)

# Successful uploads wait a full hour; failures retry much sooner so silent
# auto-start (S3 credentials still warming) does not wait another hour.
HOURLY_UPLOAD_INTERVAL_SECONDS = 3600
HOURLY_UPLOAD_RETRY_SECONDS = 300


def _ensure_file_logger() -> logging.Logger:
    """Prefer LogWriter so upload diagnostics land in logs.txt (not only stderr)."""
    global logger
    try:
        from ws_package.log.writer import LogWriter

        if not isinstance(logger, LogWriter):
            logger = LogWriter.file(__name__)
    except Exception:
        pass
    return logger


def _coerce_extra_data(extra_data: Optional[Any]) -> Dict[str, Any]:
    """Ensure extra_data is a dict (callers sometimes pass a string like stdlib logging args)."""
    if extra_data is None:
        return {}
    if isinstance(extra_data, dict):
        return extra_data
    return {"extra": extra_data}


class ThreadNameFilter(logging.Filter):
    """Filter to set threadName from extra_data if available"""
    def filter(self, record):
        # Check if thread_name is in extra_data and set threadName
        if hasattr(record, 'extra_data') and isinstance(record.extra_data, dict):
            thread_name = record.extra_data.get('thread_name')
            if thread_name:
                record.threadName = thread_name
        return True

class LogMessage:
    """Represents a log message with metadata"""
    def __init__(self, thread_name: str, level: str, message: str, 
                 module: str = "", extra_data: Optional[Dict] = None):
        self.timestamp = datetime.now()
        self.thread_name = thread_name
        self.level = level
        self.message = message
        self.module = module
        self.extra_data = _coerce_extra_data(extra_data)
        self.process_id = os.getpid()
        self.thread_id = threading.get_ident()

class ThreadLogger:
    """
    Thread-specific logger that sends messages to a dedicated logging thread
    """
    
    def __init__(self, thread_name: str, module_name: str = ""):
        self.thread_name = thread_name
        self.module_name = module_name
        self.log_queue = ThreadLogManager.get_instance().get_queue()
        
    def _log(self, level: str, message: str, extra_data: Optional[Dict] = None):
        """Send log message to logging thread"""
        log_msg = LogMessage(
            thread_name=self.thread_name,
            level=level,
            message=str(message),
            module=self.module_name,
            extra_data=extra_data
        )
        
        try:
            # Non-blocking put with timeout
            self.log_queue.put(log_msg, block=False, timeout=0.1)
        except queue.Full:
            # If queue is full, fallback to console logging
            logger.error("[FALLBACK] %s: %s - %s", self.thread_name, level, message)
    
    def debug(self, message: str, extra_data: Optional[Dict] = None):
        self._log("DEBUG", message, extra_data)
    
    def info(self, message: str, extra_data: Optional[Dict] = None):
        self._log("INFO", message, extra_data)
    
    def warning(self, message: str, extra_data: Optional[Dict] = None):
        self._log("WARNING", message, extra_data)
    
    def error(self, message: str, extra_data: Optional[Dict] = None):
        self._log("ERROR", message, extra_data)
    
    def critical(self, message: str, extra_data: Optional[Dict] = None):
        self._log("CRITICAL", message, extra_data)
    
    def exception(self, message: str, extra_data: Optional[Dict] = None):
        """Log exception with traceback"""
        import traceback
        tb = traceback.format_exc()
        extra_data = _coerce_extra_data(extra_data)
        extra_data['traceback'] = tb
        self._log("ERROR", f"{message}\n{tb}", extra_data)

class ThreadLogManager:
    """
    Singleton manager for thread-based logging system with integrated log upload functionality
    """
    
    _instance = None
    _lock = threading.Lock()
    
    def __init__(self):
        """Initialise the singleton log manager and start the background logging thread.

        Raises:
            RuntimeError: If called directly after a singleton instance already exists.
                Use ThreadLogManager.get_instance() instead.
        """
        if ThreadLogManager._instance is not None:
            raise RuntimeError("ThreadLogManager is a singleton. Use get_instance() instead.")
        
        self.log_queue = queue.Queue(maxsize=10000)  # Large queue to prevent blocking
        self.log_dir = self._get_log_directory()
        self.handlers: Dict[str, RotatingFileHandler] = {}
        self.json_handlers: Dict[str, RotatingFileHandler] = {}
        self.logging_thread = None
        self.stop_event = threading.Event()
        self.stats = {
            'total_logs': 0,
            'thread_logs': {},
            'errors': 0,
            'queue_full': 0
        }
        
        # Log upload service integration
        self.log_upload_service = None
        self.upload_enabled = False
        
        # Hourly log upload configuration
        self.hourly_upload_enabled = False
        self.last_hourly_upload_time = None
        self._hourly_upload_interval_seconds = HOURLY_UPLOAD_INTERVAL_SECONDS
        self.user_entity = None
        self.org_id = None
        self.s3_logs_path = "logs/"
        
        # Start logging thread
        self._start_logging_thread()
    
    @classmethod
    def get_instance(cls):
        """Get singleton instance"""
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = cls()
        return cls._instance
    
    def get_queue(self) -> queue.Queue:
        """Get the log queue for sending messages"""
        return self.log_queue
    
    def _get_log_directory(self) -> Path:
        """Same directory LogWriter uses so uploads find the real logs.txt."""
        try:
            from ws_package.log.writer import get_log_directory

            return get_log_directory()
        except Exception:
            pass

        if sys.platform == "darwin":
            log_dir = Path.home() / "Library/Application Support/Workstatus/logs"
        elif sys.platform == "win32":
            if getattr(sys, 'frozen', False):
                try:
                    appdata_dir = Path(os.environ.get('APPDATA', '')) / "Workstatus"
                    appdata_dir.mkdir(parents=True, exist_ok=True)
                    log_dir = appdata_dir / "logs"
                except (PermissionError, OSError):
                    log_dir = Path(sys.executable).parent / "logs"
            else:
                log_dir = Path.cwd() / "logs"
        else:
            log_dir = Path.home() / "Workstatus" / "logs"
        
        try:
            log_dir.mkdir(parents=True, exist_ok=True)
        except (PermissionError, OSError):
            # Fallback mechanism similar to LogWriter
            try:
                if sys.platform == "darwin":
                    log_dir = Path.home() / "Workstatus" / "logs"
                    log_dir.mkdir(parents=True, exist_ok=True)
                else:
                    raise
            except (PermissionError, OSError):
                import tempfile
                log_dir = Path(tempfile.gettempdir()) / "Workstatus" / "logs"
                log_dir.mkdir(parents=True, exist_ok=True)
                
        return log_dir
    
    def _start_logging_thread(self):
        """Start the dedicated logging thread"""
        self.logging_thread = threading.Thread(
            target=self._logging_worker,
            name="LoggingThread",
            daemon=True
        )
        self.logging_thread.start()
        _ensure_file_logger().info("Logging thread started. Log directory: %s", self.log_dir)
    
    def _logging_worker(self):
        """Main logging worker thread
        
        Uses exponential backoff on errors to reduce CPU usage during error conditions.
        """
        _ensure_file_logger()
        error_count = 0  # Track consecutive errors for exponential backoff
        
        while not self.stop_event.is_set():
            try:
                # Check for hourly log upload first
                self._check_hourly_log_upload()
                
                # Get log message with timeout
                try:
                    log_msg = self.log_queue.get(timeout=1.0)
                except queue.Empty:
                    continue
                
                # Process the log message
                self._process_log_message(log_msg)
                
                # Update stats
                self.stats['total_logs'] += 1
                if log_msg.thread_name not in self.stats['thread_logs']:
                    self.stats['thread_logs'][log_msg.thread_name] = 0
                self.stats['thread_logs'][log_msg.thread_name] += 1
                
                # Mark task as done
                self.log_queue.task_done()
                
                # Reset error count on success
                error_count = 0
                
            except Exception as e:
                self.stats['errors'] += 1
                logger.error("Error in logging worker: %s", e, exc_info=True)
                
                # FIXED: Exponential backoff on errors to reduce CPU usage during error conditions
                # Exponential backoff: 0.1s → 0.15s → 0.225s → ... → 2.0s max
                error_count += 1
                sleep_time = min(0.1 * (1.5 ** error_count), 2.0)
                time.sleep(sleep_time)
    
    def _process_log_message(self, log_msg: LogMessage):
        """Process a single log message"""
        try:
            # Get handlers for this thread
            txt_handler = self._get_txt_handler(log_msg.thread_name)
            json_handler = self._get_json_handler(log_msg.thread_name)
            
            # Skip empty messages to avoid meaningless log entries
            if not log_msg.message or not log_msg.message.strip():
                return
            
            # Format message
            formatted_msg = self._format_message(log_msg)
            json_msg = self._format_json_message(log_msg)
            
            # Skip empty formatted messages
            if not formatted_msg or not formatted_msg.strip():
                return
            
            # Create temporary loggers for each handler
            txt_logger = logging.getLogger(f"txt_{log_msg.thread_name}")
            json_logger = logging.getLogger(f"json_{log_msg.thread_name}")
            
            # Clear existing handlers
            txt_logger.handlers.clear()
            json_logger.handlers.clear()
            
            # Add handlers
            txt_logger.addHandler(txt_handler)
            json_logger.addHandler(json_handler)
            
            # Set levels
            txt_logger.setLevel(logging.DEBUG)
            json_logger.setLevel(logging.DEBUG)
            
            # Log messages with extra context
            # Create log record with extra_data for proper formatting
            # Note: 'threadName' is a built-in LogRecord attribute, don't include it in extra
            # Instead, we'll use a filter to set it from extra_data
            _ed = log_msg.extra_data if isinstance(log_msg.extra_data, dict) else _coerce_extra_data(log_msg.extra_data)
            extra = {
                'extra_data': {
                    **_ed,
                    'thread_name': log_msg.thread_name,  # Add thread_name to extra_data for filter
                    'module_name': log_msg.module  # Preserve module info without using reserved key
                }
            }
            
            txt_logger.info(formatted_msg, extra=extra)
            json_logger.info(json_msg, extra=extra)
            
            # Also write to main log files
            self._write_to_main_logs(log_msg, formatted_msg, json_msg)
            
        except Exception as e:
            logger.error("Error processing log message: %s", e, exc_info=True)
    
    def _check_hourly_log_upload(self):
        """Check if it's time for hourly log upload and trigger if so."""
        _ensure_file_logger()
        if self.stop_event.is_set():
            return
        if not self.hourly_upload_enabled or not self.user_entity or not self.org_id:
            return
        
        current_time = datetime.now()
        interval = self._hourly_upload_interval_seconds or HOURLY_UPLOAD_INTERVAL_SECONDS
        
        if (self.last_hourly_upload_time is None or 
            (current_time - self.last_hourly_upload_time).total_seconds() >= interval):
            
            try:
                logger.info(
                    "[LoggingThread] Starting hourly log upload at %s (dir=%s)",
                    current_time,
                    self.log_dir,
                )
                success = self._perform_hourly_log_upload()
                self.last_hourly_upload_time = current_time
                if success:
                    self._hourly_upload_interval_seconds = HOURLY_UPLOAD_INTERVAL_SECONDS
                    logger.info("[LoggingThread] Hourly log upload completed")
                else:
                    # Do not wait a full hour after a failed attempt (common in silent
                    # auto-start while STS credentials are still being persisted).
                    self._hourly_upload_interval_seconds = HOURLY_UPLOAD_RETRY_SECONDS
                    logger.warning(
                        "[LoggingThread] Hourly log upload failed — retrying in %ss",
                        HOURLY_UPLOAD_RETRY_SECONDS,
                    )
            except Exception as e:
                self.last_hourly_upload_time = current_time
                self._hourly_upload_interval_seconds = HOURLY_UPLOAD_RETRY_SECONDS
                logger.error(
                    "[LoggingThread] Error in hourly log upload: %s — retrying in %ss",
                    e,
                    HOURLY_UPLOAD_RETRY_SECONDS,
                )
    
    def _perform_hourly_log_upload(self) -> bool:
        """Upload log files to S3. Returns True if nothing failed."""
        if self.stop_event.is_set():
            return False
        try:
            # Refresh dir in case LogWriter resolved a different path after import.
            self.log_dir = self._get_log_directory()

            log_files = []
            
            for log_file in self.log_dir.glob("*.log"):
                if log_file.is_file() and log_file.stat().st_size > 0:
                    log_files.append(log_file)
            
            logs_txt_file = self.log_dir / "logs.txt"
            if logs_txt_file.is_file() and logs_txt_file.stat().st_size > 0:
                log_files.append(logs_txt_file)
            
            process_txt_file = self.log_dir / "process.txt"
            if process_txt_file.is_file() and process_txt_file.stat().st_size > 0:
                log_files.append(process_txt_file)
            
            if not log_files:
                logger.info(
                    "[LoggingThread] No log files found for upload in %s",
                    self.log_dir,
                )
                return True
            
            logger.info("[LoggingThread] Found %s log files to upload", len(log_files))
            
            uploaded = 0
            failed = 0
            for log_file in log_files:
                try:
                    self._upload_single_log_file(log_file)
                    logger.info("[LoggingThread] Successfully uploaded: %s", log_file.name)
                    self._truncate_file_after_upload(log_file)
                    logger.info("[LoggingThread] Truncated file after upload: %s", log_file.name)
                    uploaded += 1
                except Exception as e:
                    failed += 1
                    logger.error("[LoggingThread] Failed to upload %s: %s", log_file.name, e)
                    continue

            logger.info(
                "[LoggingThread] Hourly upload summary: uploaded=%s failed=%s dir=%s",
                uploaded,
                failed,
                self.log_dir,
            )
            return failed == 0
                    
        except Exception as e:
            logger.error("[LoggingThread] Error in hourly log upload process: %s", e)
            return False
    
    def _upload_single_log_file(self, log_file: Path):
        """Upload a single log file to S3"""
        if self.stop_event.is_set():
            return
        try:
            # Import here to avoid circular imports
            from ws_package.utils.s3 import S3sync, s3_detail
            from ws_package.exceptions.custom_exceptions import ScreenshotError, NetworkError, ValidationError
            import asyncio
            
            # Create destination path for S3
            timestamp = datetime.fromtimestamp(log_file.stat().st_mtime)
            filename = log_file.name
            dest_path = f"{self.s3_logs_path}{self.org_id}/{self.user_entity.user_id}/{timestamp.strftime('%Y-%m-%d-%H-%M-%S')}-{filename}"
            
            # Use asyncio to run the async upload
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            
            try:
                result = loop.run_until_complete(self._async_upload_file(str(log_file), dest_path))
                if result:
                    logger.info(
                        "[LoggingThread] Upload successful: file=%s key=%s url=%s",
                        log_file.name,
                        dest_path,
                        result,
                    )
                else:
                    raise Exception("Upload returned no result")
            finally:
                loop.close()
                
        except Exception as e:
            logger.error("[LoggingThread] Error uploading %s: %s", log_file, e)
            raise
    
    def _truncate_file_after_upload(self, log_file: Path):
        """Truncate a log file after successful upload"""
        try:
            # Only truncate if file exists and has content
            if log_file.exists() and log_file.stat().st_size > 0:
                # Open file in write mode to truncate it
                with open(log_file, 'w', encoding='utf-8') as f:
                    f.truncate(0)
                logger.info("[LoggingThread] Successfully truncated %s", log_file.name)
            else:
                logger.info("[LoggingThread] File %s is empty or doesn't exist, skipping truncation", log_file.name)
        except Exception as e:
            logger.error("[LoggingThread] Error truncating %s: %s", log_file.name, e)
            # Don't raise the exception - truncation failure shouldn't stop the process
    
    async def _async_upload_file(self, src_path: str, dest_path: str):
        """Async wrapper for file upload"""
        try:
            # Get S3 details
            from ws_package.utils.s3 import s3_detail_for_upload, S3sync
            from ws_package.exceptions.custom_exceptions import ValidationError, ScreenshotError
            
            detail = await s3_detail_for_upload(self.org_id, self.user_entity)
            if not detail:
                raise ValidationError(f"Failed to get S3 configuration for org_id: {self.org_id}")
            
            s3_sync = S3sync(detail)
            
            # Upload based on bucket type
            if self.user_entity.bucket == "hetzner":
                result = await s3_sync.upload_file_to_hetzner(src_path, dest_path)
            else:
                result = await s3_sync.upload_file(src_path, dest_path)
            
            if not result:
                raise ScreenshotError(f"Upload failed - no result returned for {src_path}")
            
            return result
            
        except Exception as e:
            logger.error("[LoggingThread] Error in async upload: %s", e)
            if 'S3_TIME_TAMPERING' in str(e) or 'RequestTimeTooSkewed' in str(e):
                logger.warning(
                    "[LoggingThread] Log upload skipped: S3 RequestTimeTooSkewed "
                    "(clock skew, not timezone mismatch): %s",
                    e,
                )
            raise
    
    def _get_txt_handler(self, thread_name: str) -> RotatingFileHandler:
        """Get or create text handler for thread with enhanced formatting"""
        if thread_name not in self.handlers:
            log_file = self.log_dir / f"{thread_name}.log"
            handler = RotatingFileHandler(
                str(log_file),
                maxBytes=10 * 1024 * 1024,  # 10MB
                backupCount=10,  # keep more history to avoid losing initial logs
                encoding='utf-8'
            )
            # Use enhanced formatter with module name and thread name support
            from ws_package.log.writer import SafeUnicodeFormatter
            handler.setFormatter(SafeUnicodeFormatter(
                '%(asctime)s - %(name)s - [%(threadName)s] - %(levelname)s - %(message)s',
                include_thread=True,
                include_extra=True
            ))
            # Add filter to set threadName from extra_data
            handler.addFilter(ThreadNameFilter())
            self.handlers[thread_name] = handler
        return self.handlers[thread_name]
    
    def _get_json_handler(self, thread_name: str) -> RotatingFileHandler:
        """Get or create JSON handler for thread - DISABLED as requested"""
        # COMMENTED OUT: JSON file creation disabled 
        # if thread_name not in self.json_handlers:
        #     log_file = self.log_dir / f"{thread_name}.json"
        #     handler = RotatingFileHandler(
        #         str(log_file),
        #         maxBytes=10 * 1024 * 1024,  # 10MB
        #         backupCount=5,
        #         encoding='utf-8'
        #     )
        #     handler.setFormatter(logging.Formatter('%(message)s'))
        #     self.json_handlers[thread_name] = handler
        # return self.json_handlers[thread_name]
        
        # Return a dummy handler that does nothing
        class DummyHandler(logging.Handler):
            def emit(self, record):
                pass
        
        return DummyHandler()
    
    def _format_message(self, log_msg: LogMessage) -> str:
        """Format log message for text output with enhanced metadata"""
        # Skip empty messages to avoid meaningless log entries
        if not log_msg.message or not log_msg.message.strip():
            return ""
        
        timestamp = log_msg.timestamp.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        
        # Use thread/listener name instead of module name for cleaner logs
        # Extract simple name from module if needed (e.g., "base" from "ws_package.models.base")
        display_name = log_msg.thread_name
        if not display_name or display_name.strip() == "":
            # Fallback: use last part of module name if thread name is empty
            if log_msg.module:
                display_name = log_msg.module.split('.')[-1] if '.' in log_msg.module else log_msg.module
            else:
                display_name = "MainThread"
        
        # Build extra context string if available
        context_parts = []
        extra = log_msg.extra_data if isinstance(log_msg.extra_data, dict) else _coerce_extra_data(log_msg.extra_data)
        if extra:
            for key, value in extra.items():
                # Skip traceback as it's usually included in message
                if key != 'traceback':
                    # Format value safely
                    if isinstance(value, (dict, list)):
                        try:
                            value = json.dumps(value, ensure_ascii=False, default=str)
                        except Exception:
                            value = str(value)
                    context_parts.append(f"{key}={value}")
        
        context_str = " | " + " | ".join(context_parts) if context_parts else ""
        
        return f"{timestamp} - {display_name} - {log_msg.level} - {log_msg.message}{context_str}"
    
    def _format_json_message(self, log_msg: LogMessage) -> str:
        """Format log message for JSON output"""
        json_data = {
            'timestamp': log_msg.timestamp.isoformat(),
            'level': log_msg.level,
            'thread_name': log_msg.thread_name,
            'module': log_msg.module,
            'message': self._safe_unicode_string(log_msg.message),
            'process_id': log_msg.process_id,
            'thread_id': log_msg.thread_id,
            'extra_data': log_msg.extra_data
        }
        return json.dumps(json_data, ensure_ascii=False)
    
    def _safe_unicode_string(self, text: str) -> str:
        """Safely handle Unicode strings"""
        if not isinstance(text, str):
            text = str(text)
        
        # Normalize Unicode characters
        text = unicodedata.normalize('NFKD', text)
        
        # Replace problematic Unicode characters
        replacements = {
            '\u2713': '[SUCCESS]',
            '\u2717': '[FAIL]',
            '\u200b': '',
            '\u200c': '',
            '\u200d': '',
            '\ufeff': '',
        }
        
        for unicode_char, replacement in replacements.items():
            text = text.replace(unicode_char, replacement)
        
        return text
    
    def _write_to_main_logs(self, log_msg: LogMessage, formatted_msg: str, json_msg: str):
        """Write to main log files with robust error handling and explicit flushing"""
        try:
            # Skip empty formatted messages to avoid meaningless log entries
            if not formatted_msg or not formatted_msg.strip():
                return
            
            # Write to main logs.txt
            main_txt_file = self.log_dir / "logs.txt"
            
            # Ensure directory exists
            try:
                main_txt_file.parent.mkdir(parents=True, exist_ok=True)
            except (PermissionError, OSError) as dir_error:
                # Log to stderr as fallback if directory creation fails
                print(f"[CRITICAL] Failed to create log directory {main_txt_file.parent}: {dir_error}", file=sys.stderr)
                return
            
            # Try to write with explicit error handling and flushing
            try:
                with open(main_txt_file, 'a', encoding='utf-8') as f:
                    f.write(formatted_msg + '\n')
                    f.flush()  # Explicitly flush to ensure write happens immediately
                    # Force write to disk (works on macOS/Linux, ignored on Windows)
                    try:
                        os.fsync(f.fileno())
                    except (OSError, AttributeError):
                        # fsync may not be available on all systems, ignore if it fails
                        pass
            except PermissionError as e:
                # Log to stderr/console as fallback when permission denied
                print(f"[CRITICAL] Permission denied writing to {main_txt_file}: {e}", file=sys.stderr)
                # Try fallback location
                try:
                    fallback_file = Path.home() / "Workstatus" / "logs" / "logs.txt"
                    fallback_file.parent.mkdir(parents=True, exist_ok=True)
                    with open(fallback_file, 'a', encoding='utf-8') as f:
                        f.write(formatted_msg + '\n')
                        f.flush()
                    print(f"[INFO] Logs written to fallback location: {fallback_file}", file=sys.stderr)
                except Exception as fallback_error:
                    print(f"[CRITICAL] Fallback log write also failed: {fallback_error}", file=sys.stderr)
            except OSError as e:
                # Log to stderr/console as fallback for OS errors
                print(f"[CRITICAL] OS error writing to {main_txt_file}: {e}", file=sys.stderr)
            except Exception as e:
                # Log to stderr/console as fallback for unexpected errors
                print(f"[CRITICAL] Unexpected error writing to {main_txt_file}: {e}", file=sys.stderr)
            
            # COMMENTED OUT: JSON file writing disabled as requested
            # # Write to main logs.json
            # main_json_file = self.log_dir / "logs.json"
            # with open(main_json_file, 'a', encoding='utf-8') as f:
            #     f.write(json_msg + '\n')
                
        except Exception as e:
            # Last resort - log to stderr and try standard logger
            print(f"[CRITICAL] Error in _write_to_main_logs: {e}", file=sys.stderr)
            # Also try to use the standard logger (might work if file handler is OK)
            try:
                logger.error("Error writing to main logs: %s", e, exc_info=True)
            except Exception:
                pass  # Last resort; if this also fails there is nowhere to write
    def get_stats(self) -> Dict[str, Any]:
        """Get logging statistics"""
        stats = {
            'total_logs': self.stats['total_logs'],
            'thread_logs': self.stats['thread_logs'].copy(),
            'errors': self.stats['errors'],
            'queue_size': self.log_queue.qsize(),
            'queue_full_count': self.stats['queue_full'],
            'active_threads': len(self.handlers),
            'log_directory': str(self.log_dir),
            'upload_enabled': self.upload_enabled
        }
        
        # Add upload service stats if available
        if self.log_upload_service:
            try:
                upload_stats = self.log_upload_service.get_upload_stats()
                stats['upload_stats'] = upload_stats
            except Exception as e:
                stats['upload_stats'] = {'error': str(e)}
        
        return stats
    
    def enable_log_upload(self, user_entity, org_id: str, s3_logs_path: str = "logs/"):
        """Enable automatic log upload to server"""
        try:
            from ws_package.services.log_upload_service import LogUploadService
            
            if self.log_upload_service:
                self.log_upload_service.stop()
            
            self.log_upload_service = LogUploadService(user_entity, org_id, s3_logs_path)
            self.upload_enabled = True
            
            logger.info("Log upload enabled for org_id: %s, user_id: %s", org_id, user_entity.user_id)
            
        except Exception as e:
            logger.error("Error enabling log upload: %s", e)
            self.upload_enabled = False
    
    def enable_hourly_log_upload(self, user_entity, org_id: str, s3_logs_path: str = "logs/"):
        """Enable hourly log upload through the logging thread"""
        _ensure_file_logger()
        try:
            self.user_entity = user_entity
            self.org_id = org_id
            self.s3_logs_path = s3_logs_path
            self.hourly_upload_enabled = True
            self.last_hourly_upload_time = None
            self._hourly_upload_interval_seconds = HOURLY_UPLOAD_INTERVAL_SECONDS
            self.log_dir = self._get_log_directory()
            
            logger.info(
                "[LoggingThread] Hourly log upload enabled for org_id: %s, user_id: %s, log_dir: %s",
                org_id,
                user_entity.user_id,
                self.log_dir,
            )
            
        except Exception as e:
            logger.error("[LoggingThread] Error enabling hourly log upload: %s", e)
            self.hourly_upload_enabled = False
    
    def disable_hourly_log_upload(self):
        """Disable hourly log upload"""
        _ensure_file_logger()
        try:
            self.hourly_upload_enabled = False
            self.user_entity = None
            self.org_id = None
            self.s3_logs_path = "logs/"
            self.last_hourly_upload_time = None
            self._hourly_upload_interval_seconds = HOURLY_UPLOAD_INTERVAL_SECONDS
            
            logger.info("[LoggingThread] Hourly log upload disabled")
            
        except Exception as e:
            logger.error("[LoggingThread] Error disabling hourly log upload: %s", e)
    
    def force_hourly_log_upload(self):
        """Force an immediate hourly log upload"""
        _ensure_file_logger()
        if self.hourly_upload_enabled and self.user_entity and self.org_id:
            try:
                logger.info("[LoggingThread] Force hourly log upload requested")
                success = self._perform_hourly_log_upload()
                self.last_hourly_upload_time = datetime.now()
                self._hourly_upload_interval_seconds = (
                    HOURLY_UPLOAD_INTERVAL_SECONDS if success else HOURLY_UPLOAD_RETRY_SECONDS
                )
                logger.info(
                    "[LoggingThread] Force hourly log upload completed (success=%s)",
                    success,
                )
            except Exception as e:
                logger.error("[LoggingThread] Error in force hourly log upload: %s", e)
        else:
            logger.error("[LoggingThread] Hourly log upload not enabled")
    
    def disable_log_upload(self):
        """Disable automatic log upload to server"""
        try:
            if self.log_upload_service:
                self.log_upload_service.stop()
                self.log_upload_service = None
            
            self.upload_enabled = False
            logger.info("Log upload disabled")
            
        except Exception as e:
            logger.error("Error disabling log upload: %s", e)
    
    def force_log_upload(self):
        """Force an immediate log upload"""
        if self.log_upload_service:
            try:
                self.log_upload_service.force_upload_now()
                logger.info("Force log upload completed")
            except Exception as e:
                logger.error("Error in force log upload: %s", e)
        else:
            logger.error("Log upload service not enabled")
    
    def stop(self):
        """Stop the logging system"""
        self.stop_event.set()
        
        # Stop log upload service if enabled
        if self.log_upload_service:
            try:
                self.log_upload_service.stop()
            except Exception as e:
                logger.error("Error stopping log upload service: %s", e)
        
        if self.logging_thread and self.logging_thread.is_alive():
            self.logging_thread.join(timeout=5)
        
        # Close all handlers
        for handler in self.handlers.values():
            handler.close()
        for handler in self.json_handlers.values():
            handler.close()

# Convenience functions for easy usage
def get_thread_logger(thread_name: str, module_name: str = "") -> ThreadLogger:
    """Get a thread-specific logger"""
    return ThreadLogger(thread_name, module_name)

def get_logger():
    return _ensure_file_logger()

def get_logging_stats() -> Dict[str, Any]:
    """Get logging statistics"""
    return ThreadLogManager.get_instance().get_stats()

def stop_logging_system():
    """Stop the logging system"""
    ThreadLogManager.get_instance().stop()

def enable_log_upload(user_entity, org_id: str, s3_logs_path: str = "logs/"):
    """Enable automatic log upload to server"""
    ThreadLogManager.get_instance().enable_log_upload(user_entity, org_id, s3_logs_path)

def enable_hourly_log_upload(user_entity, org_id: str, s3_logs_path: str = "logs/"):
    """Enable hourly log upload through the logging thread"""
    ThreadLogManager.get_instance().enable_hourly_log_upload(user_entity, org_id, s3_logs_path)

def disable_log_upload():
    """Disable automatic log upload to server"""
    ThreadLogManager.get_instance().disable_log_upload()

def disable_hourly_log_upload():
    """Disable hourly log upload"""
    ThreadLogManager.get_instance().disable_hourly_log_upload()

def force_log_upload():
    """Force an immediate log upload"""
    ThreadLogManager.get_instance().force_log_upload()

def force_hourly_log_upload():
    """Force an immediate hourly log upload"""
    ThreadLogManager.get_instance().force_hourly_log_upload()

# Module-level instance for convenience imports
thread_log_manager = ThreadLogManager.get_instance()
