"""
Centralized Thread Pool Manager for SkillDarbar API

This module provides a shared ThreadPoolExecutor to prevent thread exhaustion.
All background tasks should use this pool instead of creating raw threads.
"""

import logging
import threading
from concurrent.futures import ThreadPoolExecutor, Future
from typing import Callable, Any, Optional
from functools import wraps

logger = logging.getLogger(__name__)

# Global thread pool with limited workers to prevent thread exhaustion
# Max workers is calculated as: min(32, os.cpu_count() + 4) by default
# We set explicit limits for production stability
MAX_WORKERS = 10  # Conservative limit for shared hosting
POOL_NAME = "SkillDarbar-ThreadPool"

_thread_pool: Optional[ThreadPoolExecutor] = None
_pool_lock = threading.Lock()

# Task statistics
_task_stats = {
    'submitted': 0,
    'completed': 0,
    'failed': 0,
    'active': 0
}


def get_thread_pool() -> ThreadPoolExecutor:
    """
    Get or create the shared thread pool.
    Uses lazy initialization with thread-safe locking.
    """
    global _thread_pool
    
    if _thread_pool is None:
        with _pool_lock:
            # Double-check locking pattern
            if _thread_pool is None:
                _thread_pool = ThreadPoolExecutor(
                    max_workers=MAX_WORKERS,
                    thread_name_prefix=POOL_NAME
                )
                logger.info(f"Created shared thread pool with {MAX_WORKERS} workers")
    
    return _thread_pool


def submit_task(func: Callable, *args, **kwargs) -> Optional[Future]:
    """
    Submit a task to the shared thread pool.
    
    Args:
        func: The function to execute
        *args: Positional arguments for the function
        **kwargs: Keyword arguments for the function
        
    Returns:
        Future object or None if submission failed
    """
    global _task_stats
    
    try:
        pool = get_thread_pool()
        
        # Wrap the function to track completion
        def tracked_func(*a, **kw):
            global _task_stats
            _task_stats['active'] += 1
            try:
                result = func(*a, **kw)
                _task_stats['completed'] += 1
                return result
            except Exception as e:
                _task_stats['failed'] += 1
                logger.error(f"Task {func.__name__} failed: {e}")
                raise
            finally:
                _task_stats['active'] -= 1
        
        future = pool.submit(tracked_func, *args, **kwargs)
        _task_stats['submitted'] += 1
        
        return future
        
    except Exception as e:
        logger.error(f"Failed to submit task {func.__name__}: {e}")
        _task_stats['failed'] += 1
        return None


def submit_task_with_timeout(func: Callable, timeout: float, *args, **kwargs) -> Any:
    """
    Submit a task and wait for completion with timeout.
    
    Args:
        func: The function to execute
        timeout: Maximum seconds to wait
        *args: Positional arguments for the function
        **kwargs: Keyword arguments for the function
        
    Returns:
        Result of the function or None if timeout/error
    """
    future = submit_task(func, *args, **kwargs)
    
    if future is None:
        return None
    
    try:
        return future.result(timeout=timeout)
    except TimeoutError:
        logger.warning(f"Task {func.__name__} timed out after {timeout}s")
        return None
    except Exception as e:
        logger.error(f"Task {func.__name__} failed: {e}")
        return None


def run_in_thread_pool(timeout: Optional[float] = None):
    """
    Decorator to run a function in the shared thread pool.
    
    Args:
        timeout: Optional timeout in seconds. If None, returns Future immediately.
        
    Usage:
        @run_in_thread_pool(timeout=30)
        def my_background_task():
            # This runs in the thread pool
            pass
    """
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if timeout is not None:
                return submit_task_with_timeout(func, timeout, *args, **kwargs)
            else:
                return submit_task(func, *args, **kwargs)
        return wrapper
    return decorator


def get_pool_stats() -> dict:
    """
    Get current thread pool statistics.
    
    Returns:
        Dictionary with pool and task statistics
    """
    pool = get_thread_pool()
    
    return {
        'max_workers': MAX_WORKERS,
        'tasks_submitted': _task_stats['submitted'],
        'tasks_completed': _task_stats['completed'],
        'tasks_failed': _task_stats['failed'],
        'tasks_active': _task_stats['active'],
        'pool_name': POOL_NAME
    }


def shutdown_pool(wait: bool = True):
    """
    Shutdown the thread pool gracefully.
    
    Args:
        wait: If True, wait for pending tasks to complete
    """
    global _thread_pool
    
    with _pool_lock:
        if _thread_pool is not None:
            logger.info(f"Shutting down thread pool (wait={wait})")
            _thread_pool.shutdown(wait=wait, cancel_futures=not wait)
            _thread_pool = None
            logger.info("Thread pool shutdown complete")


# Safe wrappers for common operations that previously used raw threads

def safe_async_operation(func: Callable, timeout: float = 30, *args, **kwargs) -> bool:
    """
    Run an async operation safely with timeout.
    Returns True if successful, False otherwise.
    
    This replaces patterns like:
        thread = threading.Thread(target=func)
        thread.start()
        thread.join(timeout=timeout)
    """
    result = submit_task_with_timeout(func, timeout, *args, **kwargs)
    return result is not None


def safe_background_task(func: Callable, *args, **kwargs) -> bool:
    """
    Submit a fire-and-forget background task.
    Returns True if submitted successfully.
    
    This replaces patterns like:
        thread = threading.Thread(target=func, daemon=True)
        thread.start()
    """
    future = submit_task(func, *args, **kwargs)
    return future is not None
