# /home/Autopartes/pos/services/redis_stock_cache.py
"""Redis cache layer for inventory stock calculations.

Provides sub-millisecond stock lookups by caching SUM(inventory_operations)
results in Redis. Cache is invalidated on every stock mutation.

Fallback: if Redis is unavailable, queries PostgreSQL directly.
"""

import os
import json
import redis
from decimal import Decimal

# Connection settings from environment
REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
REDIS_STOCK_TTL = int(os.environ.get('REDIS_STOCK_TTL', '300'))  # 5 minutes default
REDIS_ENABLED = os.environ.get('REDIS_ENABLED', 'true').lower() == 'true'

# Lazy connection
_redis_client = None


def _get_redis():
    """Get or create Redis connection (lazy singleton)."""
    global _redis_client
    if _redis_client is None and REDIS_ENABLED:
        try:
            _redis_client = redis.from_url(REDIS_URL, decode_responses=True)
            _redis_client.ping()
        except Exception as e:
            print(f"[redis_stock_cache] Redis unavailable: {e}")
            _redis_client = False  # Disable for this session
    return _redis_client if _redis_client is not False else None


def _stock_key(inventory_id, branch_id=None):
    """Generate Redis key for a stock entry."""
    if branch_id:
        return f"nexus:stock:{inventory_id}:b{branch_id}"
    return f"nexus:stock:{inventory_id}"


def get_cached_stock(inventory_id, branch_id=None):
    """Get stock from Redis cache.

    Returns:
        int/None: Stock quantity if cached, None if miss or Redis down.
    """
    r = _get_redis()
    if not r:
        return None
    try:
        val = r.get(_stock_key(inventory_id, branch_id))
        if val is not None:
            return int(val)
    except Exception as e:
        print(f"[redis_stock_cache] GET error: {e}")
    return None


def set_cached_stock(inventory_id, quantity, branch_id=None):
    """Store stock in Redis cache with TTL."""
    r = _get_redis()
    if not r:
        return
    try:
        key = _stock_key(inventory_id, branch_id)
        r.set(key, int(quantity), ex=REDIS_STOCK_TTL)
    except Exception as e:
        print(f"[redis_stock_cache] SET error: {e}")


def invalidate_stock(inventory_id, branch_id=None):
    """Remove stock entry from Redis cache.

    Called after any inventory mutation (sale, purchase, adjust, transfer).
    If branch_id is None, invalidates both global and branch-specific keys.
    """
    r = _get_redis()
    if not r:
        return
    try:
        keys = [_stock_key(inventory_id)]
        if branch_id:
            keys.append(_stock_key(inventory_id, branch_id))
        else:
            # Wildcard invalidation for all branches of this item
            pattern = _stock_key(inventory_id, '*')
            keys = r.keys(pattern)
            keys.append(_stock_key(inventory_id))
        if keys:
            r.delete(*keys)
    except Exception as e:
        print(f"[redis_stock_cache] DELETE error: {e}")


def invalidate_all_stock():
    """Flush all stock keys from Redis. Use with caution (e.g., after bulk import)."""
    r = _get_redis()
    if not r:
        return
    try:
        keys = r.keys('nexus:stock:*')
        if keys:
            r.delete(*keys)
            print(f"[redis_stock_cache] Flushed {len(keys)} stock keys")
    except Exception as e:
        print(f"[redis_stock_cache] FLUSH error: {e}")


def health_check():
    """Return True if Redis is reachable."""
    r = _get_redis()
    if not r:
        return False
    try:
        return r.ping()
    except Exception:
        return False
