"""
Cache decorators for service methods
"""
import time
import logging
from functools import wraps
from typing import Callable, Optional, List

from .cache_manager import get_cache
from .keys import generate_cache_key

logger = logging.getLogger(__name__)


def cached(cache_type: str, ttl: Optional[int] = None, key_params: Optional[List[str]] = None):
    """
    Decorator for caching service method results with performance tracking

    Usage:
        @cached(cache_type='dashboard_summary', key_params=['company', 'username'])
        async def get_complete_summary(company: str, username: str):
            # ... Oracle query logic ...

    Features:
        - Automatic cache key generation from function parameters
        - Performance timing (cache hit vs miss)
        - Benchmark tracking for time saved calculation
        - Per-user cache enable/disable
        - Global cache toggle
        - Transparent - zero changes to function logic

    Args:
        cache_type: Type of cache (used for TTL lookup and stats)
        ttl: Optional custom TTL (overrides config default)
        key_params: List of parameter names to include in cache key

    Returns:
        Decorated async function
    """
    def decorator(func: Callable):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            start_time = time.time()
            cache = get_cache()

            # Extract username for per-user settings
            username = _extract_username(args, kwargs, key_params)

            # Check if cache is enabled (global + per-user)
            cache_enabled = await cache.is_enabled_for_user(username) if cache else False

            if not cache or not cache_enabled:
                # Cache disabled - execute directly
                result = await func(*args, **kwargs)
                elapsed_ms = (time.time() - start_time) * 1000

                # Set metadata in request.state if available (for API responses)
                if 'request' in kwargs and hasattr(kwargs['request'], 'state'):
                    kwargs['request'].state.cache_hit = False
                    kwargs['request'].state.response_time_ms = elapsed_ms
                    kwargs['request'].state.cache_source = None

                if cache and cache.config.track_performance:
                    await cache.track_performance(
                        cache_type=cache_type,
                        is_hit=False,
                        actual_time_ms=elapsed_ms,
                        username=username
                    )

                return result

            # Generate cache key from function parameters
            cache_key = generate_cache_key(cache_type, key_params, args, kwargs)

            # Try to get from cache
            cached_value = await cache.get(cache_key, cache_type)

            if cached_value is not None:
                # ✅ CACHE HIT
                elapsed_ms = (time.time() - start_time) * 1000

                # Set metadata in request.state if available (for API responses)
                if 'request' in kwargs and hasattr(kwargs['request'], 'state'):
                    cache_source_value = cache.get_last_cache_source()  # L1 or L2
                    kwargs['request'].state.cache_hit = True
                    kwargs['request'].state.response_time_ms = elapsed_ms
                    kwargs['request'].state.cache_source = cache_source_value

                # Get benchmark for calculating time saved
                benchmark = await cache.get_benchmark(cache_type)
                time_saved_ms = (benchmark - elapsed_ms) if benchmark else None

                # Track performance
                if cache.config.track_performance:
                    await cache.track_performance(
                        cache_type=cache_type,
                        is_hit=True,
                        actual_time_ms=elapsed_ms,
                        time_saved_ms=time_saved_ms,
                        estimated_oracle_time_ms=benchmark,
                        company_id=_extract_company_id(args, kwargs, key_params),
                        username=username
                    )

                return cached_value

            # ❌ CACHE MISS - execute function (query Oracle)
            result = await func(*args, **kwargs)
            elapsed_ms = (time.time() - start_time) * 1000

            # Set metadata in request.state if available (for API responses)
            if 'request' in kwargs and hasattr(kwargs['request'], 'state'):
                kwargs['request'].state.cache_hit = False
                kwargs['request'].state.response_time_ms = elapsed_ms
                kwargs['request'].state.cache_source = None

            # Update benchmark with real Oracle time
            await cache.update_benchmark(cache_type, elapsed_ms)

            # Track performance
            if cache.config.track_performance:
                await cache.track_performance(
                    cache_type=cache_type,
                    is_hit=False,
                    actual_time_ms=elapsed_ms,
                    company_id=_extract_company_id(args, kwargs, key_params),
                    username=username
                )

            # Store in cache for next time
            company_id = _extract_company_id(args, kwargs, key_params)
            await cache.set(cache_key, result, cache_type, company_id, ttl)

            return result

        return wrapper
    return decorator


def _extract_username(args, kwargs, key_params: Optional[List[str]]) -> Optional[str]:
    """
    Extract username from function parameters (args or kwargs)

    Checks:
    1. key_params position in args (if username is in key_params)
    2. Direct username in kwargs
    3. current_user object in kwargs
    4. user object in kwargs
    5. request.state.user (from AuthenticationMiddleware)

    Args:
        args: Positional arguments
        kwargs: Keyword arguments
        key_params: List of parameter names (for finding position in args)

    Returns:
        Username string or None
    """
    # Try to find username in args based on key_params position
    if key_params and 'username' in key_params:
        try:
            username_idx = key_params.index('username')
            if username_idx < len(args):
                username = args[username_idx]
                if username:
                    return str(username)
        except (ValueError, IndexError):
            pass

    # Direct username parameter in kwargs
    if 'username' in kwargs:
        return kwargs['username']

    # Current user object (from FastAPI Depends)
    if 'current_user' in kwargs:
        user = kwargs['current_user']
        if hasattr(user, 'username'):
            return user.username
        elif isinstance(user, dict) and 'username' in user:
            return user['username']
        return str(user)

    # User object
    if 'user' in kwargs:
        user = kwargs['user']
        if hasattr(user, 'username'):
            return user.username
        elif isinstance(user, dict) and 'username' in user:
            return user['username']
        return str(user)

    # Extract from request.state.user (set by AuthenticationMiddleware)
    if 'request' in kwargs:
        request = kwargs['request']
        if hasattr(request, 'state') and hasattr(request.state, 'user'):
            user = request.state.user
            if hasattr(user, 'username'):
                return user.username
            elif isinstance(user, dict) and 'username' in user:
                return user['username']

    return None


def _extract_company_id(args, kwargs, key_params: Optional[List[str]]) -> Optional[int]:
    """
    Extract company_id from function parameters for cache indexing

    Tries multiple approaches:
    1. Direct company_id in kwargs
    2. company parameter (converted to int)
    3. Positional args based on key_params position

    Args:
        args: Positional arguments
        kwargs: Keyword arguments
        key_params: List of parameter names

    Returns:
        Company ID as integer or None
    """
    # Try kwargs first
    if 'company_id' in kwargs:
        try:
            return int(kwargs['company_id'])
        except (ValueError, TypeError):
            pass

    if 'company' in kwargs:
        try:
            return int(kwargs['company'])
        except (ValueError, TypeError):
            pass

    # Try positional args based on key_params
    if key_params:
        if 'company_id' in key_params:
            idx = key_params.index('company_id')
            if idx < len(args):
                try:
                    return int(args[idx])
                except (ValueError, TypeError):
                    pass

        elif 'company' in key_params:
            idx = key_params.index('company')
            if idx < len(args):
                try:
                    return int(args[idx])
                except (ValueError, TypeError):
                    pass

    return None
