# Copyright (c) 2026 MergeGuide, Inc. All rights reserved.
# SPDX-License-Identifier: Proprietary
"""Module-level cache instances for Lambda warm invocations.

This module provides singleton cache instances that persist across
warm Lambda invocations, reducing DynamoDB load and improving response times.

Cache Types:
- Policy cache: 5 min TTL for policy definitions
- Org cache: 10 min TTL for organization configurations
- Install cache: 15 min TTL for GitHub App installation data

Usage:
    from mergeguide.cache import get_policy_cache, invalidate_org

    # Get cached policies
    cache = get_policy_cache()
    rules = cache.get(f"policy:{org_id}")

    # Invalidate all caches for an org
    invalidate_org(org_id)

Cache Keys:
- policy:{org_id} - List of Rule objects for an organization
- org:{org_id} - Organization configuration dict
- install:{installation_id} - GitHub App installation dict
"""

from __future__ import annotations

import logging
import os
from typing import TYPE_CHECKING, Any

from .file_cache import CachedEvaluation, FileCache
from .ttl_cache import TTLCache

if TYPE_CHECKING:
    from mergeguide.engine.rules import Rule

logger = logging.getLogger(__name__)

# Environment variable to disable caching (for debugging/testing)
CACHE_DISABLED = os.environ.get("MERGEGUIDE_CACHE_DISABLED", "").lower() in (
    "true",
    "1",
    "yes",
)

# TTL values in seconds (configurable via environment)
POLICY_TTL = int(os.environ.get("MERGEGUIDE_POLICY_CACHE_TTL", "300"))  # 5 min
ORG_TTL = int(os.environ.get("MERGEGUIDE_ORG_CACHE_TTL", "600"))  # 10 min
INSTALL_TTL = int(os.environ.get("MERGEGUIDE_INSTALL_CACHE_TTL", "900"))  # 15 min

# Max cache sizes
MAX_CACHE_SIZE = int(os.environ.get("MERGEGUIDE_MAX_CACHE_SIZE", "1000"))

# Global cache instances (persist across warm invocations)
_policy_cache: TTLCache[list[Rule]] | None = None
_org_cache: TTLCache[dict[str, Any]] | None = None
_install_cache: TTLCache[dict[str, Any]] | None = None


def get_policy_cache() -> TTLCache[list[Rule]]:
    """Get or create policy cache (5 min TTL).

    Returns the singleton policy cache instance. The cache is created
    on first access and persists across warm Lambda invocations.

    Returns:
        TTLCache instance for storing policy Rule lists.
    """
    global _policy_cache
    if _policy_cache is None:
        _policy_cache = TTLCache[list](
            default_ttl_seconds=POLICY_TTL,
            max_size=MAX_CACHE_SIZE,
        )
        logger.debug(f"Initialized policy cache (TTL={POLICY_TTL}s)")
    return _policy_cache


def get_org_cache() -> TTLCache[dict[str, Any]]:
    """Get or create org config cache (10 min TTL).

    Returns the singleton organization cache instance for storing
    organization configuration data.

    Returns:
        TTLCache instance for storing org config dicts.
    """
    global _org_cache
    if _org_cache is None:
        _org_cache = TTLCache[dict](
            default_ttl_seconds=ORG_TTL,
            max_size=MAX_CACHE_SIZE,
        )
        logger.debug(f"Initialized org cache (TTL={ORG_TTL}s)")
    return _org_cache


def get_install_cache() -> TTLCache[dict[str, Any]]:
    """Get or create installation cache (15 min TTL).

    Returns the singleton installation cache instance for storing
    GitHub App installation data.

    Returns:
        TTLCache instance for storing installation config dicts.
    """
    global _install_cache
    if _install_cache is None:
        _install_cache = TTLCache[dict](
            default_ttl_seconds=INSTALL_TTL,
            max_size=MAX_CACHE_SIZE,
        )
        logger.debug(f"Initialized install cache (TTL={INSTALL_TTL}s)")
    return _install_cache


def invalidate_org(org_id: str) -> int:
    """Invalidate all caches for an organization.

    Removes all cached data related to the specified organization.
    Call this when organization data is modified (policy saved,
    org config updated, etc.).

    Args:
        org_id: The organization ID to invalidate.

    Returns:
        Total number of cache entries invalidated.
    """
    total_invalidated = 0

    policy_cache = get_policy_cache()
    total_invalidated += policy_cache.invalidate(f"policy:{org_id}")

    org_cache = get_org_cache()
    total_invalidated += 1 if org_cache.invalidate(f"org:{org_id}") else 0

    logger.debug(f"Invalidated {total_invalidated} cache entries for org {org_id}")
    return total_invalidated


def invalidate_installation(installation_id: str) -> bool:
    """Invalidate cached installation data.

    Args:
        installation_id: The GitHub App installation ID to invalidate.

    Returns:
        True if the entry was found and removed.
    """
    install_cache = get_install_cache()
    result = install_cache.invalidate(f"install:{installation_id}")
    if result:
        logger.debug(f"Invalidated cache for installation {installation_id}")
    return result


def get_all_stats() -> dict[str, dict]:
    """Get combined cache statistics.

    Returns statistics from all cache instances, useful for
    monitoring and debugging.

    Returns:
        Dict with stats for each cache type (policy, org, install).
    """
    return {
        "policy": get_policy_cache().stats(),
        "org": get_org_cache().stats(),
        "install": get_install_cache().stats(),
    }


def clear_all_caches() -> None:
    """Clear all cache instances.

    Use with caution - this removes all cached data. Primarily
    useful for testing or when cache needs to be fully reset.
    """
    get_policy_cache().clear()
    get_org_cache().clear()
    get_install_cache().clear()
    logger.info("Cleared all caches")


def is_cache_disabled() -> bool:
    """Check if caching is disabled via environment variable.

    Returns:
        True if MERGEGUIDE_CACHE_DISABLED is set to true/1/yes.
    """
    return CACHE_DISABLED


# Export public API
__all__ = [
    "CachedEvaluation",
    "FileCache",
    "TTLCache",
    "get_policy_cache",
    "get_org_cache",
    "get_install_cache",
    "invalidate_org",
    "invalidate_installation",
    "get_all_stats",
    "clear_all_caches",
    "is_cache_disabled",
]
