"""
CacheManager - Centralized cache operations manager.

Handles cache retrieval, storage, and validation for route handlers.
Uses thread pools for non-blocking async operations.
"""

import threading
import copy
from concurrent.futures import ThreadPoolExecutor
from flask import current_app
from flask_babel import force_locale
from wedeliver_core_plus.helpers.caching.cache_logger import (
    cache_debug, cache_info, cache_warning, cache_error
)


class CacheManager:
    """
    Manages cache operations for route handlers.

    Features:
    - Async cache writes using thread pool (non-blocking)
    - Cache validation in background
    - Cross-service metadata handling
    - Thread-safe operations
    """

    # Global thread pool for async cache writes
    # Reused across all requests for better resource management
    # Initialized lazily with config-based pool size
    _write_pool = None
    _write_pool_initialized = False

    @classmethod
    def _get_write_pool(cls):
        """Get or create the write thread pool with config-based size."""
        if not cls._write_pool_initialized:
            try:
                pool_size = current_app.config.get('CACHE_WRITE_POOL_SIZE', 10)
            except:
                pool_size = 10

            cls._write_pool = ThreadPoolExecutor(
                max_workers=pool_size,
                thread_name_prefix="cache-write"
            )
            cls._write_pool_initialized = True
            cache_debug(f"Initialized write pool with {pool_size} workers")

        return cls._write_pool
    
    def __init__(self, cache_rule, path, request_data):
        """
        Initialize CacheManager.
        
        Args:
            cache_rule: Cache rule class (not instance)
            path: API endpoint path
            request_data: Request parameters (validated_data)
        """
        self.cache_rule = cache_rule
        self.path = path
        self.request_data = request_data
        self.cache_instance = None
        self.cache_key = None
        
    def initialize(self):
        """
        Initialize cache instance and generate cache key.

        Returns:
            bool: True if cache is configured and initialized, False otherwise
        """
        if not self.cache_rule:
            return False

        # Check if cache is enabled - skip initialization if disabled
        from wedeliver_core_plus.helpers.caching.cache_invalidation_registry import is_cache_enabled
        if not is_cache_enabled():
            return False

        try:
            # Initialize the cache rule instance
            self.cache_instance = self.cache_rule(self.path)

            # Check if cache instance was successfully created
            if self.cache_instance is None or self.cache_instance.cache is None:
                return False

            # Generate cache key
            self.cache_key = self.cache_instance.make_key(self.request_data)

            # Store cross-service metadata for MicroFetcher
            self._store_cross_service_metadata()

            return True
        except Exception as e:
            cache_warning(f"Initialization failed: {e}")
            return False
    
    def get_cached_response(self):
        """
        Retrieve cached response if available.
        
        Returns:
            Cached response data with is_cached flag, or None if cache miss
        """
        if not self.cache_instance or not self.cache_key:
            return None
            
        try:
            cached_data = self.cache_instance.get(self.cache_key)
            if cached_data is None:
                cache_debug(f"MISS for {self.path}")
                return None
                
            cache_debug(f"HIT for {self.path}")
            
            # Handle cache validation if enabled (async, non-blocking)
            self._validate_cache_async(cached_data)
            
            # Return response with is_cached flag
            return self._prepare_cached_response(cached_data)
            
        except Exception as e:
            cache_warning(f"Error retrieving cache: {e}")
            return None
    
    def store_response_async(self, output):
        """
        Store response in cache asynchronously (non-blocking).

        Uses thread pool to prevent blocking the response.

        Args:
            output: Serialized response data to cache
        """
        if not self.cache_instance or not self.cache_key or output is None:
            return

        # Check if async writes are enabled
        try:
            async_writes = current_app.config.get('CACHE_ASYNC_WRITES', True)
        except:
            async_writes = True

        if async_writes:
            # Submit to thread pool (returns immediately, non-blocking)
            write_pool = self._get_write_pool()
            write_pool.submit(self._store_cache, self.cache_key, output)
            cache_debug(f"Queued async cache write for {self.path}")
        else:
            # Synchronous write (blocking)
            self._store_cache(self.cache_key, output)
            cache_debug(f"Sync cache write for {self.path}")
    
    def _store_cache(self, cache_key, data):
        """
        Internal method to store cache (runs in background thread).
        
        Args:
            cache_key: Cache key
            data: Data to cache
        """
        try:
            self.cache_instance.set(cache_key, data)
            cache_debug(f"✓ Async stored: {cache_key}")
        except Exception as e:
            cache_warning(f"✗ Async store failed for {cache_key}: {e}")
    
    def _prepare_cached_response(self, cached_data):
        """
        Prepare cached response with is_cached flag.
        
        Args:
            cached_data: Raw cached data
            
        Returns:
            Response data with is_cached flag added
        """
        # Make a deep copy to prevent validation thread from seeing the flag
        response_data = copy.deepcopy(cached_data)
        
        # Add is_cached flag
        if isinstance(response_data, dict):
            response_data["is_cached"] = True
        elif isinstance(response_data, list):
            # Add flag to each dict element in list
            for item in response_data:
                if isinstance(item, dict):
                    item["is_cached"] = True
        
        return response_data
    
    def _validate_cache_async(self, cached_data):
        """
        Validate cached data asynchronously in background thread.

        Strategy: Fetch fresh data in MAIN THREAD (where request context is available),
        then compare in BACKGROUND THREAD (no context needed).

        This prevents "Working outside of request context" errors.

        Args:
            cached_data: Cached data to validate
        """
        try:
            validation_mode = current_app.config.get('CACHE_VALIDATION_MODE', 'off')

            if validation_mode == 'off':
                return

            if not self.cache_instance._should_validate(validation_mode):
                return

            # Capture config values and app instance in main thread
            metrics_enabled = current_app.config.get('CACHE_VALIDATION_METRICS_ENABLED', True)
            app = current_app._get_current_object()

            # ============================================================
            # FETCH FRESH DATA + COMPARE IN BACKGROUND THREAD
            # ============================================================
            def _async_validate():
                """
                Background validation function.

                Runs with app context to allow database queries and business logic execution.
                The app context provides access to app.config, database connections, etc.
                """

                with app.app_context():
                    with force_locale(self._user_language):
                        try:
                            self.cache_instance._validate_cache_data(
                                cache_key=self.cache_key,
                                cached_data=cached_data,
                                route_handler_func=self._route_handler_func,
                                validated_data=self.request_data,
                                schema=self._schema,
                                many=self._many,
                                metrics_enabled=metrics_enabled,
                                app=app  # Pass app instance for context
                            )
                        except Exception as e:
                            cache_warning(f"Background validation error: {e}")

            # Run validation in background thread (non-blocking)
            validation_thread = threading.Thread(target=_async_validate, daemon=True)
            validation_thread.start()

        except Exception as e:
            cache_warning(f"Error setting up cache validation: {e}")
    
    def set_validation_context(self, route_handler_func, schema, many, user_language):
        """
        Set context needed for cache validation.
        
        Args:
            route_handler_func: The route handler function
            schema: Marshmallow schema for serialization
            many: Boolean flag for list serialization
        """
        self._route_handler_func = route_handler_func
        self._schema = schema
        self._many = many
        self._user_language = user_language or 'ar'
    
    def _store_cross_service_metadata(self):
        """
        Store cross-service cache metadata in Flask g for MicroFetcher.

        This allows MicroFetcher to piggyback cache invalidation metadata
        when making cross-service calls.

        Uses model names (e.g., "Customer") instead of full paths.
        """
        try:
            # Extract cross-service models from model_invalidation_conditions
            cross_service_models = {}

            if hasattr(self.cache_instance, 'model_invalidation_conditions') and \
               self.cache_instance.model_invalidation_conditions:

                for model_key, conditions in self.cache_instance.model_invalidation_conditions.items():
                    if isinstance(model_key, str):
                        is_local = conditions.get("local", True)
                        if not is_local:
                            # Store with model name (not full path)
                            cross_service_models[model_key] = conditions

            if cross_service_models:
                from wedeliver_core_plus.helpers.caching.valkey_redis_utils import BaseCacheRule
                _store_cache_metadata_in_context(
                    service_name=BaseCacheRule._service_name,
                    api_path=self.path,
                    scoped_cache_keys=self.cache_instance.scoped_cache_keys,
                    cross_service_models=cross_service_models
                )
        except Exception as e:
            cache_warning(f"Error storing cross-service metadata: {e}")


def _store_cache_metadata_in_context(service_name, api_path, scoped_cache_keys, cross_service_models):
    """
    Store cache metadata in Flask g context.
    
    Args:
        service_name: Name of the service
        api_path: API endpoint path
        scoped_cache_keys: Scoped cache keys configuration
        cross_service_models: Cross-service model invalidation conditions
    """
    from flask import g
    
    if not hasattr(g, '_cache_registration_metadata'):
        g._cache_registration_metadata = []
    
    g._cache_registration_metadata.append({
        'service_name': service_name,
        'api_path': api_path,
        'scoped_cache_keys': scoped_cache_keys,
        'cross_service_models': cross_service_models
    })

