"""Security settings utilities.

Provides functions to load security settings from JSON defaults with
environment variable overrides. Used during app initialization before
the full settings system is available.
"""

import json
from functools import lru_cache
from pathlib import Path
from typing import Any, Optional, TypeVar, Union

from loguru import logger

from local_deep_research.settings.manager import check_env_setting

T = TypeVar("T", int, float, str, bool)

# Path to security settings JSON
_SETTINGS_PATH = (
    Path(__file__).parent.parent / "defaults" / "settings_security.json"
)


@lru_cache(maxsize=1)
def _load_security_settings() -> dict:
    """Load and cache security settings from JSON file.

    Returns:
        Dictionary of security settings, or empty dict on error.
    """
    try:
        if _SETTINGS_PATH.exists():
            with open(_SETTINGS_PATH, "r") as f:
                return json.load(f)
    except Exception:
        logger.warning(
            f"Failed to load security settings from {_SETTINGS_PATH}"
        )
    return {}


def _convert_value(value: str, target_type: type, key: str) -> Optional[Any]:
    """Convert string value to target type with proper error handling.

    Args:
        value: String value to convert
        target_type: Target type (int, float, str, bool)
        key: Setting key for error messages

    Returns:
        Converted value or None if conversion fails
    """
    try:
        if target_type is bool:
            # Handle boolean strings properly
            return value.lower() in ("true", "1", "yes", "on")
        if target_type is int:
            return int(value)
        if target_type is float:
            return float(value)
        return str(value)
    except (ValueError, TypeError):
        logger.warning(
            f"Invalid value for {key}: '{value}' cannot be converted to {target_type.__name__}"
        )
        return None


def _validate_bounds(
    value: Union[int, float],
    min_value: Optional[Union[int, float]],
    max_value: Optional[Union[int, float]],
    key: str,
) -> Union[int, float]:
    """Validate that value is within min/max bounds.

    Args:
        value: Value to validate
        min_value: Minimum allowed value (inclusive)
        max_value: Maximum allowed value (inclusive)
        key: Setting key for error messages

    Returns:
        Clamped value within bounds
    """
    original = value

    if min_value is not None and value < min_value:
        value = min_value
        logger.warning(
            f"Value {original} for {key} is below minimum {min_value}, using {value}"
        )

    if max_value is not None and value > max_value:
        value = max_value
        logger.warning(
            f"Value {original} for {key} is above maximum {max_value}, using {value}"
        )

    return value


def get_security_default(key: str, default: T) -> T:
    """Load a security setting with environment variable override.

    Priority order:
    1. Environment variable (LDR_SECURITY_<KEY>)
    2. JSON defaults file value
    3. Provided default

    Environment variables are validated against min/max bounds defined
    in the JSON settings file.

    Args:
        key: Setting key (e.g., "security.session_remember_me_days")
        default: Default value if setting not found (also determines type)

    Returns:
        Setting value of same type as default

    Example:
        >>> get_security_default("security.session_remember_me_days", 30)
        30  # or value from env/JSON
    """
    settings = _load_security_settings()
    setting_data = settings.get(key, {})

    # Get min/max bounds from settings schema
    min_value = (
        setting_data.get("min_value")
        if isinstance(setting_data, dict)
        else None
    )
    max_value = (
        setting_data.get("max_value")
        if isinstance(setting_data, dict)
        else None
    )

    # Check environment variable first (e.g., LDR_SECURITY_SESSION_REMEMBER_ME_DAYS)
    env_key = f"LDR_{key.upper().replace('.', '_')}"
    env_value = check_env_setting(key)

    if env_value is not None:
        converted = _convert_value(env_value, type(default), env_key)
        if converted is not None:
            # Validate bounds for numeric types
            if isinstance(converted, (int, float)) and (
                min_value is not None or max_value is not None
            ):
                converted = _validate_bounds(
                    converted, min_value, max_value, env_key
                )
            return converted  # type: ignore

    # Load from JSON defaults
    if key in settings:
        if isinstance(setting_data, dict) and "value" in setting_data:
            return setting_data["value"]
        return setting_data

    return default
