"""Typed cache helpers backed by Redis.

All failures are silent (logged as warnings) so Redis being down never causes an outage.

Key design:
  profile:{user_id}                  — Profile row
  user:{user_id}                     — User view row
  linkedin_account:{user_id}         — Active LinkedInAccount row
  profile_analysis:{user_id}         — ProfileAnalysis row
  company:{company_id}               — Company row
  linkedin_posts:{user_id}           — List[LinkedInPost] (scraped reference posts)
  post_types:{user_id}:1 or :0       — List[PostType] (active_only=True/False)
  post_type:{post_type_id}           — Single PostType row
  gen_post:{post_id}                 — Single GeneratedPost row
  gen_posts:{user_id}                — List[GeneratedPost] for user
"""
import json
from typing import Any, Optional
from loguru import logger

# TTL constants (seconds)
PROFILE_TTL = 300           # 5 min  — updated on settings / onboarding changes
USER_TTL = 300              # 5 min
LINKEDIN_ACCOUNT_TTL = 300  # 5 min  — updated only on OAuth connect/disconnect
PROFILE_ANALYSIS_TTL = 600  # 10 min — computed infrequently by background job
COMPANY_TTL = 300           # 5 min  — company settings
LINKEDIN_POSTS_TTL = 600    # 10 min — scraped reference data, rarely changes
POST_TYPES_TTL = 600        # 10 min — strategy config, rarely changes
POST_TYPE_TTL = 600         # 10 min
GEN_POST_TTL = 120          # 2 min  — status/content changes frequently
GEN_POSTS_TTL = 120         # 2 min


class CacheService:
    """Redis-backed cache with typed key helpers and silent failure semantics."""

    # ------------------------------------------------------------------
    # Key helpers
    # ------------------------------------------------------------------

    def profile_key(self, user_id: str) -> str:
        return f"profile:{user_id}"

    def user_key(self, user_id: str) -> str:
        return f"user:{user_id}"

    def linkedin_account_key(self, user_id: str) -> str:
        return f"linkedin_account:{user_id}"

    def profile_analysis_key(self, user_id: str) -> str:
        return f"profile_analysis:{user_id}"

    def company_key(self, company_id: str) -> str:
        return f"company:{company_id}"

    def linkedin_posts_key(self, user_id: str) -> str:
        return f"linkedin_posts:{user_id}"

    def post_types_key(self, user_id: str, active_only: bool = True) -> str:
        return f"post_types:{user_id}:{'1' if active_only else '0'}"

    def post_type_key(self, post_type_id: str) -> str:
        return f"post_type:{post_type_id}"

    def gen_post_key(self, post_id: str) -> str:
        return f"gen_post:{post_id}"

    def gen_posts_key(self, user_id: str) -> str:
        return f"gen_posts:{user_id}"

    # ------------------------------------------------------------------
    # Core operations
    # ------------------------------------------------------------------

    async def get(self, key: str) -> Optional[Any]:
        try:
            from src.services.redis_client import get_redis
            r = await get_redis()
            value = await r.get(key)
            if value is not None:
                return json.loads(value)
            return None
        except Exception as e:
            logger.warning(f"Cache get failed for {key}: {e}")
            return None

    async def set(self, key: str, value: Any, ttl: int):
        try:
            from src.services.redis_client import get_redis
            r = await get_redis()
            await r.setex(key, ttl, json.dumps(value, default=str))
        except Exception as e:
            logger.warning(f"Cache set failed for {key}: {e}")

    async def delete(self, *keys: str):
        if not keys:
            return
        try:
            from src.services.redis_client import get_redis
            r = await get_redis()
            await r.delete(*keys)
        except Exception as e:
            logger.warning(f"Cache delete failed for {keys}: {e}")

    # ------------------------------------------------------------------
    # Compound invalidation helpers
    # ------------------------------------------------------------------

    async def invalidate_profile(self, user_id: str):
        """Invalidate profile, user view, profile_analysis, and linkedin_account."""
        await self.delete(
            self.profile_key(user_id),
            self.user_key(user_id),
            self.profile_analysis_key(user_id),
            self.linkedin_account_key(user_id),
        )

    async def invalidate_company(self, company_id: str):
        await self.delete(self.company_key(company_id))

    async def invalidate_linkedin_posts(self, user_id: str):
        """Invalidate the scraped LinkedIn posts list for a user."""
        await self.delete(self.linkedin_posts_key(user_id))

    async def invalidate_post_types(self, user_id: str):
        """Invalidate both active_only variants of the post types list."""
        await self.delete(
            self.post_types_key(user_id, True),
            self.post_types_key(user_id, False),
        )

    async def invalidate_post_type(self, post_type_id: str, user_id: str):
        """Invalidate a single PostType row + both list variants."""
        await self.delete(
            self.post_type_key(post_type_id),
            self.post_types_key(user_id, True),
            self.post_types_key(user_id, False),
        )

    async def invalidate_gen_post(self, post_id: str, user_id: str):
        """Invalidate a single GeneratedPost + the user's post list."""
        await self.delete(
            self.gen_post_key(post_id),
            self.gen_posts_key(user_id),
        )

    async def invalidate_gen_posts(self, user_id: str):
        """Invalidate only the user's GeneratedPost list (not a single entry)."""
        await self.delete(self.gen_posts_key(user_id))

    async def invalidate_linkedin_account(self, user_id: str):
        """Invalidate the LinkedIn account cache for a user."""
        await self.delete(self.linkedin_account_key(user_id))


# Global singleton
cache = CacheService()
