"""Redis Configuration for Empathy Framework

Handles connection to Redis from environment variables.
Supports Redis Cloud, Railway, local Docker, managed Redis, or mock mode.

Environment Variables:
    REDIS_MODE: "cloud" or "local" (inferred from REDIS_HOST if not set)
        - cloud: Uses REDIS_HOST, REDIS_PORT, REDIS_PASSWORD (for managed services)
        - local: Uses localhost:6379, no password (for local Redis OSS / Docker)
    REDIS_URL: Full Redis URL (redis://user:pass@host:port) - overrides REDIS_MODE
    REDIS_HOST: Redis host (used in cloud mode)
    REDIS_PORT: Redis port (default: 6379)
    REDIS_PASSWORD: Redis password (used in cloud mode, ignored in local mode)
    REDIS_DB: Redis database number (default: 0)
    EMPATHY_REDIS_MOCK: Set to "true" to use mock mode

    # SSL/TLS (for managed Redis services)
    REDIS_SSL: Set to "true" to enable SSL
    REDIS_SSL_CERT_REQS: Certificate requirement ("required", "optional", "none")
    REDIS_SSL_CA_CERTS: Path to CA certificate file
    REDIS_SSL_CERTFILE: Path to client certificate
    REDIS_SSL_KEYFILE: Path to client key

    # Connection settings
    REDIS_SOCKET_TIMEOUT: Socket timeout in seconds (default: 5.0)
    REDIS_MAX_CONNECTIONS: Connection pool size (default: 10)

    # Retry settings
    REDIS_RETRY_MAX_ATTEMPTS: Max retry attempts (default: 3)
    REDIS_RETRY_BASE_DELAY: Base retry delay in seconds (default: 0.1)
    REDIS_RETRY_MAX_DELAY: Max retry delay in seconds (default: 2.0)

    # Sentinel (for high availability)
    REDIS_SENTINEL_HOSTS: Comma-separated host:port pairs
    REDIS_SENTINEL_MASTER: Sentinel master name

Railway Auto-Detection:
    When deployed on Railway, REDIS_URL is automatically set.
    For Railway Redis with SSL, the URL starts with "rediss://"

Usage:
    from attune.redis_config import get_redis_memory

    # Automatically uses environment variables
    memory = get_redis_memory()

    # Or with explicit URL (SSL auto-detected from rediss://)
    memory = get_redis_memory(url="rediss://user:pass@managed-redis.com:6379")

    # Or with explicit config
    from attune.memory.short_term import RedisConfig
    config = RedisConfig(host="localhost", ssl=True)
    memory = get_redis_memory(config=config)

Copyright 2025 Smart AI Memory, LLC
Licensed under Fair Source 0.9
"""

import logging
import os
from urllib.parse import urlparse

from .memory.short_term import RedisConfig, RedisShortTermMemory

logger = logging.getLogger(__name__)

# Valid REDIS_MODE values
REDIS_MODE_CLOUD = "cloud"
REDIS_MODE_LOCAL = "local"
_VALID_REDIS_MODES = {REDIS_MODE_CLOUD, REDIS_MODE_LOCAL}
_LOCAL_HOSTS = {"localhost", "127.0.0.1", ""}


def _resolve_redis_mode() -> str:
    """Resolve the effective Redis mode from environment.

    Priority:
    1. Explicit REDIS_MODE env var ("cloud" or "local")
    2. Inference from REDIS_HOST:
       - Non-localhost host -> "cloud"
       - localhost/127.0.0.1 or missing -> "local"

    Returns:
        "cloud" or "local"

    Raises:
        ValueError: If REDIS_MODE has an invalid value

    """
    explicit_mode = os.getenv("REDIS_MODE", "").strip().lower()

    if explicit_mode:
        if explicit_mode not in _VALID_REDIS_MODES:
            raise ValueError(
                f"Invalid REDIS_MODE='{explicit_mode}'. "
                f"Valid values: {', '.join(sorted(_VALID_REDIS_MODES))}"
            )
        return explicit_mode

    # Infer from REDIS_HOST
    redis_host = os.getenv("REDIS_HOST", "").strip()
    if redis_host and redis_host not in _LOCAL_HOSTS:
        return REDIS_MODE_CLOUD

    return REDIS_MODE_LOCAL


def parse_redis_url(url: str) -> dict:
    """Parse Redis URL into connection parameters.

    Supports:
    - redis://user:pass@host:port/db (standard)
    - rediss://user:pass@host:port/db (SSL enabled)

    Args:
        url: Redis URL (redis:// or rediss://)

    Returns:
        Dict with host, port, password, db, ssl

    """
    parsed = urlparse(url)

    # Detect SSL from scheme
    ssl = parsed.scheme == "rediss"

    return {
        "host": parsed.hostname or "localhost",
        "port": parsed.port or 6379,
        "password": parsed.password,
        "db": int(parsed.path.lstrip("/") or 0) if parsed.path else 0,
        "ssl": ssl,
    }


def _get_common_connection_kwargs() -> dict:
    """Read common connection/retry env vars shared by all modes.

    Returns:
        Dict of connection settings for RedisConfig constructor.

    """
    return {
        "socket_timeout": float(os.getenv("REDIS_SOCKET_TIMEOUT", "5.0")),
        "socket_connect_timeout": float(os.getenv("REDIS_SOCKET_CONNECT_TIMEOUT", "5.0")),
        "max_connections": int(os.getenv("REDIS_MAX_CONNECTIONS", "10")),
        "retry_on_timeout": os.getenv("REDIS_RETRY_ON_TIMEOUT", "true").lower() == "true",
        "retry_max_attempts": int(os.getenv("REDIS_RETRY_MAX_ATTEMPTS", "3")),
        "retry_base_delay": float(os.getenv("REDIS_RETRY_BASE_DELAY", "0.1")),
        "retry_max_delay": float(os.getenv("REDIS_RETRY_MAX_DELAY", "2.0")),
    }


def get_redis_config() -> RedisConfig:
    """Get Redis configuration from environment variables.

    Priority:
    1. EMPATHY_REDIS_MOCK=true -> mock mode
    2. REDIS_URL (full URL, used by Railway/Heroku/managed services)
    3. REDIS_MODE + individual env vars:
       - "cloud": uses REDIS_HOST, REDIS_PORT, REDIS_PASSWORD
       - "local": uses localhost:6379, no password (ignores REDIS_PASSWORD)
       - If REDIS_MODE not set, inferred from REDIS_HOST

    Returns:
        RedisConfig with all connection parameters

    Raises:
        ValueError: If REDIS_MODE has an invalid value

    """
    # Check for mock mode
    if os.getenv("EMPATHY_REDIS_MOCK", "").lower() == "true":
        return RedisConfig(use_mock=True)

    # Check for full URL (Railway, Heroku, managed services)
    redis_url = os.getenv("REDIS_URL") or os.getenv("REDIS_PRIVATE_URL")
    if redis_url:
        url_config = parse_redis_url(redis_url)
        return RedisConfig(
            host=url_config["host"],
            port=url_config["port"],
            password=url_config["password"],
            db=url_config["db"],
            ssl=url_config.get("ssl", False),
            use_mock=False,
            **_get_common_connection_kwargs(),
        )

    # Resolve mode (explicit REDIS_MODE or inferred from REDIS_HOST)
    mode = _resolve_redis_mode()
    common = _get_common_connection_kwargs()

    if mode == REDIS_MODE_LOCAL:
        # Local mode: localhost, no password, ignore REDIS_PASSWORD
        return RedisConfig(
            host="localhost",
            port=int(os.getenv("REDIS_PORT", "6379")),
            password=None,
            db=int(os.getenv("REDIS_DB", "0")),
            use_mock=False,
            ssl=False,
            **common,
        )

    # Cloud mode: use REDIS_HOST, REDIS_PORT, REDIS_PASSWORD from env
    host = os.getenv("REDIS_HOST", "")
    password = os.getenv("REDIS_PASSWORD")

    if not host:
        logger.warning(
            "REDIS_MODE=cloud but REDIS_HOST not set. "
            "Set REDIS_HOST to your Redis Cloud endpoint."
        )

    if not password:
        logger.warning(
            "REDIS_MODE=cloud but REDIS_PASSWORD not set. "
            "Most cloud Redis services require a password."
        )

    return RedisConfig(
        host=host or "localhost",
        port=int(os.getenv("REDIS_PORT", "6379")),
        password=password,
        db=int(os.getenv("REDIS_DB", "0")),
        use_mock=False,
        # SSL settings
        ssl=os.getenv("REDIS_SSL", "").lower() == "true",
        ssl_cert_reqs=os.getenv("REDIS_SSL_CERT_REQS"),
        ssl_ca_certs=os.getenv("REDIS_SSL_CA_CERTS"),
        ssl_certfile=os.getenv("REDIS_SSL_CERTFILE"),
        ssl_keyfile=os.getenv("REDIS_SSL_KEYFILE"),
        **common,
    )


def get_redis_config_dict() -> dict:
    """Get Redis configuration as a dictionary (legacy compatibility).

    Returns:
        Dict with connection parameters

    """
    config = get_redis_config()
    return {
        "host": config.host,
        "port": config.port,
        "password": config.password,
        "db": config.db,
        "use_mock": config.use_mock,
        "ssl": config.ssl,
    }


def get_redis_memory(
    url: str | None = None,
    use_mock: bool | None = None,
    config: RedisConfig | None = None,
) -> RedisShortTermMemory:
    """Create a RedisShortTermMemory instance with environment-based config.

    Args:
        url: Optional explicit Redis URL (overrides env vars)
        use_mock: Optional explicit mock mode (overrides env vars)
        config: Optional explicit RedisConfig (overrides all other options)

    Returns:
        Configured RedisShortTermMemory instance

    Examples:
        # Auto-configure from environment
        memory = get_redis_memory()

        # Explicit URL (SSL auto-detected from rediss://)
        memory = get_redis_memory(url="rediss://user:pass@managed-redis.com:6379")

        # Force mock mode
        memory = get_redis_memory(use_mock=True)

        # Explicit config with all options
        from attune.memory.short_term import RedisConfig
        config = RedisConfig(
            host="redis.example.com",
            port=6379,
            ssl=True,
            retry_max_attempts=5,
        )
        memory = get_redis_memory(config=config)

    """
    # Explicit config takes highest priority
    if config is not None:
        return RedisShortTermMemory(config=config)

    # Explicit mock mode
    if use_mock is True:
        return RedisShortTermMemory(use_mock=True)

    # Explicit URL
    if url:
        url_config = parse_redis_url(url)
        redis_config = RedisConfig(
            host=url_config["host"],
            port=url_config["port"],
            password=url_config["password"],
            db=url_config["db"],
            ssl=url_config.get("ssl", False),
            use_mock=False,
        )
        return RedisShortTermMemory(config=redis_config)

    # Environment-based config
    env_config = get_redis_config()
    return RedisShortTermMemory(config=env_config)


def check_redis_connection() -> dict:
    """Check Redis connection and return status.

    Returns:
        Dict with connection status and info

    Example:
        >>> status = check_redis_connection()
        >>> if status["connected"]:
        ...     print(f"Connected to {status['host']}:{status['port']}")

    """
    config = get_redis_config()

    # Resolve mode for reporting
    try:
        redis_mode = _resolve_redis_mode()
    except ValueError:
        redis_mode = "unknown"

    result = {
        "config_source": "environment",
        "redis_mode": redis_mode,
        "use_mock": config.use_mock,
        "host": config.host,
        "port": config.port,
        "has_password": bool(config.password),
        "db": config.db,
        "connected": False,
        "error": None,
    }

    # Determine config source
    if os.getenv("REDIS_URL"):
        result["config_source"] = "REDIS_URL"
    elif os.getenv("REDIS_PRIVATE_URL"):
        result["config_source"] = "REDIS_PRIVATE_URL"
    elif os.getenv("REDIS_MODE"):
        result["config_source"] = f"REDIS_MODE={os.getenv('REDIS_MODE')}"
    elif os.getenv("REDIS_HOST"):
        result["config_source"] = "REDIS_HOST"

    if result["use_mock"]:
        result["connected"] = True
        result["config_source"] = "mock_mode"
        return result

    try:
        memory = get_redis_memory()
        result["connected"] = memory.ping()
        if result["connected"]:
            stats = memory.get_stats()
            result["memory_used"] = stats.get("used_memory")
            result["total_keys"] = stats.get("total_keys")
    except Exception as e:
        result["error"] = str(e)

    return result


# Convenience function for Railway deployments
def get_railway_redis() -> RedisShortTermMemory:
    """Get Redis configured for Railway deployment.

    Railway automatically sets REDIS_URL when you add a Redis service.

    Returns:
        RedisShortTermMemory configured for Railway

    Raises:
        EnvironmentError: If REDIS_URL is not set

    """
    redis_url = os.getenv("REDIS_URL") or os.getenv("REDIS_PRIVATE_URL")

    if not redis_url:
        raise OSError(
            "REDIS_URL not found. Make sure Redis is added to your Railway project.\n"
            "Run: railway add --database redis",
        )

    return get_redis_memory(url=redis_url)
