# SPDX-License-Identifier: Apache-2.0
"""
LMCache Controller Configuration

Configuration system for LMCache Controller that:
- Loads configuration from YAML file or environment variables
- Supports command-line parameter overrides
- Provides thread-safe singleton pattern for global access
"""

# Standard
from typing import Any, Dict, Optional
import json

# First Party
from lmcache.logging import init_logger
from lmcache.v1.config_base import (
    create_config_class,
    create_singleton_config,
    load_config_with_overrides,
)

logger = init_logger(__name__)


# Controller-specific configuration definitions
_CONTROLLER_CONFIG_DEFINITIONS: dict[str, dict[str, Any]] = {
    # Basic controller configurations
    "controller_monitor_ports": {
        "type": Optional[dict],
        "default": '{"pull": 8300, "reply": 8400}',
        "env_converter": lambda x: (
            x if isinstance(x, dict) else json.loads(x) if x else None
        ),
        "description": "JSON string of monitor ports",
    },
    "controller_host": {
        "type": str,
        "default": "0.0.0.0",
        "env_converter": str,
        "description": "Controller host address",
    },
    "controller_port": {
        "type": int,
        "default": 9000,
        "env_converter": int,
        "description": "Controller API server port",
    },
    "health_check_interval": {
        "type": int,
        "default": -1,
        "env_converter": int,
        "description": "Health check interval in seconds (-1 = disabled)",
    },
    "lmcache_worker_timeout": {
        "type": int,
        "default": 300,
        "env_converter": int,
        "description": "LMCache worker timeout in seconds",
    },
    # Extra configurations
    "extra_config": {
        "type": Optional[dict],
        "default": None,
        "env_converter": lambda x: (
            x if isinstance(x, dict) else json.loads(x) if x else None
        ),
        "description": "Extra configuration parameters",
    },
}


# Specialized methods that are unique to ControllerConfig
def _validate_config(self):
    """Validate configuration parameters"""
    # Validate timeouts
    if self.health_check_interval != -1 and self.health_check_interval < 1:
        raise ValueError(f"Invalid health_check_interval: {self.health_check_interval}")
    return self


def _log_config(self):
    """Log configuration"""
    config_dict = {}
    for name in _CONTROLLER_CONFIG_DEFINITIONS:
        value = getattr(self, name)
        config_dict[name] = value

    logger.info(f"Controller Configuration: {config_dict}")
    return self


def _post_init(self):
    """Post-initialization setup"""
    pass


# Create configuration class using the base utility
ControllerConfig = create_config_class(
    config_name="ControllerConfig",
    config_definitions=_CONTROLLER_CONFIG_DEFINITIONS,
    namespace_extras={
        "validate": _validate_config,
        "log_config": _log_config,
        "__post_init__": _post_init,
    },
    env_prefix="LMCACHE_CONTROLLER_",
)


# Create singleton getter using the base utility
controller_get_or_create_config = create_singleton_config(
    getter_func_name="controller_get_or_create_config",
    config_class=ControllerConfig,
    config_env_var="LMCACHE_CONTROLLER_CONFIG_FILE",
)


def override_controller_config_from_dict(
    config: "ControllerConfig",  # type: ignore[valid-type]
    overrides: dict[str, Any],
):
    """Override configuration with dictionary"""
    for key, value in overrides.items():
        if hasattr(config, key):
            old_value = getattr(config, key)

            # Check if this field has an env_converter in the definitions
            if key in _CONTROLLER_CONFIG_DEFINITIONS:
                env_converter = _CONTROLLER_CONFIG_DEFINITIONS[key].get("env_converter")
                if env_converter:
                    try:
                        # Apply the env_converter to the value
                        converted_value = env_converter(value)
                        setattr(config, key, converted_value)
                    except (ValueError, json.JSONDecodeError) as e:
                        logger.warning(f"Failed to convert {key}={value!r}: {e}")
                        # Keep the original value if conversion fails
                        setattr(config, key, value)
                else:
                    setattr(config, key, value)
            else:
                setattr(config, key, value)

            new_value = getattr(config, key)
            if old_value != new_value:
                logger.info(
                    f"Override controller config: {key} = {new_value} (was {old_value})"
                )
        else:
            logger.warning(f"Unknown controller config key: {key}, ignoring")


def load_controller_config_with_overrides(
    config_file_path: Optional[str] = None,
    overrides: Optional[Dict[str, Any]] = None,
) -> "ControllerConfig":  # type: ignore[valid-type]
    """
    Load controller configuration with support for file, env vars, and overrides.

    This function uses the generic load_config_with_overrides utility from
    config_base.py to reduce code duplication.

    Args:
        config_file_path: Optional direct path to config file
        overrides: Optional dictionary of configuration overrides

    Returns:
        Loaded and validated ControllerConfig instance
    """
    return load_config_with_overrides(
        config_class=ControllerConfig,
        config_file_env_var="LMCACHE_CONTROLLER_CONFIG_FILE",
        config_file_path=config_file_path,
        overrides=overrides,
    )
