"""
Utilities for managing settings in the programmatic API.

This module provides functions to create settings snapshots for the API
without requiring database access, reusing the same mechanisms as the
web interface.
"""

import os
import copy
from typing import Any, Dict, Optional, Union
from loguru import logger

from ..settings import SettingsManager
from ..settings.base import ISettingsManager


class InMemorySettingsManager(ISettingsManager):
    """
    In-memory settings manager that doesn't require database access.

    This is used for the programmatic API to provide settings without
    needing a database connection.
    """

    # Type mapping from UI elements to Python types (same as SettingsManager)
    _UI_ELEMENT_TO_SETTING_TYPE = {
        "text": str,
        # JSON should already be parsed
        "json": lambda x: x,
        "password": str,
        "select": str,
        "number": float,
        "range": float,
        "checkbox": bool,
    }

    def __init__(self):
        """Initialize with default settings from JSON file."""
        # Create a base manager to get default settings
        self._base_manager = SettingsManager(db_session=None)
        self._settings = {}
        self._load_defaults()

    def _get_typed_value(self, setting_data: Dict[str, Any], value: Any) -> Any:
        """
        Convert a value to the appropriate type based on the setting's ui_element.

        Args:
            setting_data: The setting metadata containing ui_element
            value: The value to convert

        Returns:
            The typed value, or the original value if conversion fails
        """
        ui_element = setting_data.get("ui_element", "text")
        setting_type = self._UI_ELEMENT_TO_SETTING_TYPE.get(ui_element)

        if setting_type is None:
            logger.warning(
                f"Unknown ui_element type: {ui_element}, returning value as-is"
            )
            return value

        try:
            # Special handling for checkbox/bool with string values
            if ui_element == "checkbox" and isinstance(value, str):
                return value.lower() in ("true", "1", "yes", "on")
            return setting_type(value)
        except (ValueError, TypeError) as e:
            logger.warning(
                f"Failed to convert value {value} to type {setting_type}: {e}"
            )
            return value

    def _load_defaults(self):
        """Load default settings from the JSON file."""
        # Get default settings from the base manager
        defaults = self._base_manager.default_settings

        # Convert to the format expected by get_all_settings
        for key, setting_data in defaults.items():
            self._settings[key] = setting_data.copy()

            # Check environment variable override
            env_key = f"LDR_{key.upper().replace('.', '_')}"
            env_value = os.environ.get(env_key)
            if env_value is not None:
                # Use the typed value conversion
                self._settings[key]["value"] = self._get_typed_value(
                    setting_data, env_value
                )

    def get_setting(
        self, key: str, default: Any = None, check_env: bool = True
    ) -> Any:
        """Get a setting value."""
        if key in self._settings:
            setting_data = self._settings[key]
            value = setting_data.get("value", default)
            # Ensure the value has the correct type
            return self._get_typed_value(setting_data, value)
        return default

    def set_setting(self, key: str, value: Any, commit: bool = True) -> bool:
        """Set a setting value (in memory only)."""
        if key in self._settings:
            # Validate and convert the value to the correct type
            typed_value = self._get_typed_value(self._settings[key], value)
            self._settings[key]["value"] = typed_value
            return True
        return False

    def get_all_settings(self) -> Dict[str, Any]:
        """Get all settings with metadata."""
        return copy.deepcopy(self._settings)

    def load_from_defaults_file(
        self, commit: bool = True, **kwargs: Any
    ) -> None:
        """Reload defaults (already done in __init__)."""
        self._load_defaults()

    def create_or_update_setting(
        self, setting: Union[Dict[str, Any], Any], commit: bool = True
    ) -> Optional[Any]:
        """Create or update a setting (in memory only)."""
        if isinstance(setting, dict) and "key" in setting:
            key = setting["key"]
            # If the setting has a value, ensure it has the correct type
            if "value" in setting:
                typed_value = self._get_typed_value(setting, setting["value"])
                setting = setting.copy()  # Don't modify the original
                setting["value"] = typed_value
            self._settings[key] = setting
            return setting
        return None

    def delete_setting(self, key: str, commit: bool = True) -> bool:
        """Delete a setting (in memory only)."""
        if key in self._settings:
            del self._settings[key]
            return True
        return False

    def import_settings(
        self,
        settings_data: Dict[str, Any],
        commit: bool = True,
        overwrite: bool = True,
        delete_extra: bool = False,
    ) -> None:
        """Import settings from a dictionary."""
        if delete_extra:
            self._settings.clear()

        for key, value in settings_data.items():
            if overwrite or key not in self._settings:
                # Ensure proper type handling for imported settings
                if isinstance(value, dict) and "value" in value:
                    typed_value = self._get_typed_value(value, value["value"])
                    value = value.copy()
                    value["value"] = typed_value
                self._settings[key] = value


def get_default_settings_snapshot() -> Dict[str, Any]:
    """
    Get a complete settings snapshot with default values.

    This uses the same mechanism as the web interface but without
    requiring database access. Environment variables are checked
    for overrides.

    Returns:
        Dict mapping setting keys to their values and metadata
    """
    manager = InMemorySettingsManager()
    return manager.get_all_settings()


def create_settings_snapshot(
    base_settings: Optional[Dict[str, Any]] = None,
    overrides: Optional[Dict[str, Any]] = None,
    **kwargs,
) -> Dict[str, Any]:
    """
    Create a settings snapshot for the programmatic API.

    Args:
        base_settings: Base settings dict (defaults to get_default_settings_snapshot())
        overrides: Dict of setting overrides (e.g., {"llm.provider": "openai"})
        **kwargs: Common setting shortcuts:
            - provider: Maps to "llm.provider"
            - api_key: Maps to "llm.{provider}.api_key"
            - temperature: Maps to "llm.temperature"
            - max_search_results: Maps to "search.max_results"
            - search_engines: Maps to enabled search engines

    Returns:
        Complete settings snapshot for use with the API
    """
    # Start with base settings or defaults
    if base_settings is None:
        settings = get_default_settings_snapshot()
    else:
        settings = copy.deepcopy(base_settings)

    # Apply overrides if provided
    if overrides:
        for key, value in overrides.items():
            if key in settings:
                if isinstance(settings[key], dict) and "value" in settings[key]:
                    settings[key]["value"] = value
                else:
                    settings[key] = value
            else:
                # Create a simple setting entry for unknown keys
                settings[key] = {"value": value}

    # Handle common kwargs shortcuts
    if "provider" in kwargs:
        provider = kwargs["provider"]
        if "llm.provider" in settings:
            settings["llm.provider"]["value"] = provider
        else:
            settings["llm.provider"] = {"value": provider}

        # Handle api_key if provided
        if "api_key" in kwargs:
            api_key = kwargs["api_key"]
            api_key_setting = f"llm.{provider}.api_key"
            if api_key_setting in settings:
                settings[api_key_setting]["value"] = api_key
            else:
                settings[api_key_setting] = {"value": api_key}

    if "temperature" in kwargs:
        if "llm.temperature" in settings:
            settings["llm.temperature"]["value"] = kwargs["temperature"]
        else:
            settings["llm.temperature"] = {"value": kwargs["temperature"]}

    if "max_search_results" in kwargs:
        if "search.max_results" in settings:
            settings["search.max_results"]["value"] = kwargs[
                "max_search_results"
            ]
        else:
            settings["search.max_results"] = {
                "value": kwargs["max_search_results"]
            }

    # Add any other common shortcuts here...

    return settings


def extract_setting_value(
    settings_snapshot: Dict[str, Any], key: str, default: Any = None
) -> Any:
    """
    Extract a setting value from a settings snapshot.

    Args:
        settings_snapshot: Settings snapshot dict
        key: Setting key (e.g., "llm.provider")
        default: Default value if not found

    Returns:
        The setting value
    """
    if settings_snapshot is None:
        return default
    if key in settings_snapshot:
        setting = settings_snapshot[key]
        if isinstance(setting, dict) and "value" in setting:
            return setting["value"]
        return setting
    return default
