"""
Subscription Cache Service
Provides Redis-based caching for subscription data with 10-minute TTL
"""

import structlog
from typing import Optional, Dict, Any
import json
from datetime import datetime, timezone

from app.repositories import SubscriptionRepository
from app.models.tenants import Subscription

logger = structlog.get_logger()

# Cache TTL in seconds (10 minutes)
SUBSCRIPTION_CACHE_TTL = 600


class SubscriptionCacheService:
    """Service for cached subscription lookups"""

    def __init__(self, redis_client=None, database_manager=None):
        self.redis = redis_client
        self.database_manager = database_manager

    async def ensure_database_manager(self):
        """Ensure database manager is properly initialized"""
        if self.database_manager is None:
            from app.core.config import settings
            from shared.database.base import create_database_manager
            self.database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")

    async def get_tenant_tier_cached(self, tenant_id: str) -> str:
        """
        Get tenant subscription tier with Redis caching

        Args:
            tenant_id: Tenant ID

        Returns:
            Subscription tier (starter, professional, enterprise)
        """
        try:
            # Ensure database manager is initialized
            await self.ensure_database_manager()

            cache_key = f"subscription:tier:{tenant_id}"

            # Try to get from cache
            if self.redis:
                try:
                    cached_tier = await self.redis.get(cache_key)
                    if cached_tier:
                        logger.debug("Subscription tier cache hit", tenant_id=tenant_id, tier=cached_tier)
                        return cached_tier.decode('utf-8') if isinstance(cached_tier, bytes) else cached_tier
                except Exception as e:
                    logger.warning("Redis cache read failed, falling back to database",
                                 tenant_id=tenant_id, error=str(e))

            # Cache miss or Redis unavailable - fetch from database
            logger.debug("Subscription tier cache miss", tenant_id=tenant_id)

            async with self.database_manager.get_session() as db_session:
                subscription_repo = SubscriptionRepository(Subscription, db_session)
                subscription = await subscription_repo.get_active_subscription(tenant_id)

                if not subscription:
                    logger.warning("No active subscription found, returning starter tier",
                                 tenant_id=tenant_id)
                    return "starter"

                tier = subscription.plan

                # Cache the result
                if self.redis:
                    try:
                        await self.redis.setex(cache_key, SUBSCRIPTION_CACHE_TTL, tier)
                        logger.debug("Cached subscription tier", tenant_id=tenant_id, tier=tier)
                    except Exception as e:
                        logger.warning("Failed to cache subscription tier",
                                     tenant_id=tenant_id, error=str(e))

                return tier

        except Exception as e:
            logger.error("Failed to get subscription tier",
                        tenant_id=tenant_id, error=str(e))
            return "starter"  # Fallback to starter on error

    async def get_tenant_subscription_cached(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """
        Get full tenant subscription with Redis caching

        Args:
            tenant_id: Tenant ID

        Returns:
            Subscription data as dictionary or None
        """
        try:
            # Ensure database manager is initialized
            await self.ensure_database_manager()

            cache_key = f"subscription:full:{tenant_id}"

            # Try to get from cache
            if self.redis:
                try:
                    cached_data = await self.redis.get(cache_key)
                    if cached_data:
                        logger.debug("Subscription cache hit", tenant_id=tenant_id)
                        data = json.loads(cached_data.decode('utf-8') if isinstance(cached_data, bytes) else cached_data)
                        return data
                except Exception as e:
                    logger.warning("Redis cache read failed, falling back to database",
                                 tenant_id=tenant_id, error=str(e))

            # Cache miss or Redis unavailable - fetch from database
            logger.debug("Subscription cache miss", tenant_id=tenant_id)

            async with self.database_manager.get_session() as db_session:
                subscription_repo = SubscriptionRepository(Subscription, db_session)
                subscription = await subscription_repo.get_active_subscription(tenant_id)

                if not subscription:
                    logger.warning("No active subscription found", tenant_id=tenant_id)
                    return None

                # Convert to dictionary
                subscription_data = {
                    "id": str(subscription.id),
                    "tenant_id": str(subscription.tenant_id),
                    "plan": subscription.plan,
                    "status": subscription.status,
                    "monthly_price": subscription.monthly_price,
                    "billing_cycle": subscription.billing_cycle,
                    "next_billing_date": subscription.next_billing_date.isoformat() if subscription.next_billing_date else None,
                    "trial_ends_at": subscription.trial_ends_at.isoformat() if subscription.trial_ends_at else None,
                    "cancelled_at": subscription.cancelled_at.isoformat() if subscription.cancelled_at else None,
                    "cancellation_effective_date": subscription.cancellation_effective_date.isoformat() if subscription.cancellation_effective_date else None,
                    "max_users": subscription.max_users,
                    "max_locations": subscription.max_locations,
                    "max_products": subscription.max_products,
                    "features": subscription.features,
                    "created_at": subscription.created_at.isoformat() if subscription.created_at else None,
                    "updated_at": subscription.updated_at.isoformat() if subscription.updated_at else None
                }

                # Cache the result
                if self.redis:
                    try:
                        await self.redis.setex(
                            cache_key,
                            SUBSCRIPTION_CACHE_TTL,
                            json.dumps(subscription_data)
                        )
                        logger.debug("Cached subscription data", tenant_id=tenant_id)
                    except Exception as e:
                        logger.warning("Failed to cache subscription data",
                                     tenant_id=tenant_id, error=str(e))

                return subscription_data

        except Exception as e:
            logger.error("Failed to get subscription",
                        tenant_id=tenant_id, error=str(e))
            return None

    async def invalidate_subscription_cache(self, tenant_id: str) -> None:
        """
        Invalidate subscription cache for a tenant

        Args:
            tenant_id: Tenant ID
        """
        try:
            if not self.redis:
                logger.debug("Redis not available, skipping cache invalidation",
                           tenant_id=tenant_id)
                return

            tier_key = f"subscription:tier:{tenant_id}"
            full_key = f"subscription:full:{tenant_id}"

            # Delete both cache keys
            await self.redis.delete(tier_key, full_key)

            logger.info("Invalidated subscription cache",
                       tenant_id=tenant_id)

        except Exception as e:
            logger.warning("Failed to invalidate subscription cache",
                         tenant_id=tenant_id, error=str(e))

    async def warm_cache(self, tenant_id: str) -> None:
        """
        Pre-warm the cache by loading subscription data

        Args:
            tenant_id: Tenant ID
        """
        try:
            logger.debug("Warming subscription cache", tenant_id=tenant_id)

            # Load both tier and full subscription to cache
            await self.get_tenant_tier_cached(tenant_id)
            await self.get_tenant_subscription_cached(tenant_id)

            logger.info("Subscription cache warmed", tenant_id=tenant_id)

        except Exception as e:
            logger.warning("Failed to warm subscription cache",
                         tenant_id=tenant_id, error=str(e))


# Singleton instance for easy access
_cache_service_instance: Optional[SubscriptionCacheService] = None


def get_subscription_cache_service(redis_client=None) -> SubscriptionCacheService:
    """
    Get or create subscription cache service singleton

    Args:
        redis_client: Optional Redis client

    Returns:
        SubscriptionCacheService instance
    """
    global _cache_service_instance

    if _cache_service_instance is None:
        from shared.redis_utils import initialize_redis
        from app.core.config import settings
        import asyncio
        
        # Initialize Redis client if not provided
        redis_client_instance = None
        if redis_client is None:
            try:
                loop = asyncio.get_event_loop()
                if not loop.is_running():
                    redis_client_instance = asyncio.run(initialize_redis(settings.REDIS_URL))
                else:
                    # If event loop is running, we can't use asyncio.run
                    # This is a limitation, but we'll handle it by not initializing Redis here
                    pass
            except:
                pass
        else:
            redis_client_instance = redis_client
            
        _cache_service_instance = SubscriptionCacheService(redis_client=redis_client_instance)
    elif redis_client is not None and _cache_service_instance.redis is None:
        _cache_service_instance.redis = redis_client

    return _cache_service_instance
