"""
Configuration file for search engines.
Loads search engine definitions from the user's configuration.
"""

import json
from typing import Any, Dict, List, Optional
from sqlalchemy.orm import Session

from loguru import logger

from ..config.thread_settings import get_setting_from_snapshot
from ..utilities.db_utils import get_settings_manager


def _get_setting(
    key: str,
    default_value: Any = None,
    db_session: Optional[Session] = None,
    settings_snapshot: Optional[Dict[str, Any]] = None,
    username: Optional[str] = None,
) -> Any:
    """
    Get a setting from either a database session or settings snapshot.

    Args:
        key: The setting key
        default_value: Default value if setting not found
        db_session: Database session for direct access
        settings_snapshot: Settings snapshot for thread context
        username: Username for backward compatibility

    Returns:
        The setting value or default_value if not found
    """
    # Try settings snapshot first (thread context)
    if settings_snapshot:
        try:
            return get_setting_from_snapshot(
                key, default_value, settings_snapshot=settings_snapshot
            )
        except Exception as e:
            logger.debug(f"Could not get setting {key} from snapshot: {e}")

    # Try database session if available
    if db_session:
        try:
            settings_manager = get_settings_manager(db_session, username)
            return settings_manager.get_setting(key, default_value)
        except Exception as e:
            logger.debug(f"Could not get setting {key} from db_session: {e}")

    # Return default if all methods fail
    logger.warning(
        f"Could not retrieve setting '{key}', returning default: {default_value}"
    )
    return default_value


def _extract_per_engine_config(
    raw_config: Dict[str, Any],
) -> Dict[str, Dict[str, Any]]:
    """
    Converts the "flat" configuration loaded from the settings database into
    individual settings dictionaries for each engine.

    Args:
        raw_config: The raw "flat" configuration.

    Returns:
        Configuration dictionaries indexed by engine name.

    """
    nested_config = {}
    for key, value in raw_config.items():
        if "." in key:
            # This is a higher-level key.
            top_level_key = key.split(".")[0]
            lower_keys = ".".join(key.split(".")[1:])
            nested_config.setdefault(top_level_key, {})[lower_keys] = value
        else:
            # This is a low-level key.
            nested_config[key] = value

    # Expand all the lower-level keys.
    for key, value in nested_config.items():
        if isinstance(value, dict):
            # Expand the child keys.
            nested_config[key] = _extract_per_engine_config(value)

    return nested_config


def search_config(
    username: Optional[str] = None,
    db_session: Optional[Session] = None,
    settings_snapshot: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    Returns the search engine configuration loaded from the database or settings snapshot.

    Args:
        username: Username for backward compatibility (deprecated)
        db_session: Database session for direct access (preferred for web routes)
        settings_snapshot: Settings snapshot for thread context (preferred for background threads)

    Returns:
        The search engine configuration loaded from the database or snapshot.
    """
    # Extract search engine definitions
    config_data = _get_setting(
        "search.engine.web",
        {},
        db_session=db_session,
        settings_snapshot=settings_snapshot,
        username=username,
    )

    search_engines = _extract_per_engine_config(config_data)
    search_engines["auto"] = _get_setting(
        "search.engine.auto",
        {},
        db_session=db_session,
        settings_snapshot=settings_snapshot,
        username=username,
    )

    # Add registered retrievers as available search engines
    from .retriever_registry import retriever_registry

    for name in retriever_registry.list_registered():
        search_engines[name] = {
            "module_path": ".engines.search_engine_retriever",
            "class_name": "RetrieverSearchEngine",
            "requires_api_key": False,
            "requires_llm": False,
            "description": f"LangChain retriever: {name}",
            "strengths": [
                "Domain-specific knowledge",
                "No rate limits",
                "Fast retrieval",
            ],
            "weaknesses": ["Limited to indexed content"],
            "supports_full_search": True,
            "is_retriever": True,  # Mark as retriever for identification
        }

    logger.info(
        f"Loaded {len(search_engines)} search engines from configuration file"
    )
    logger.info(f"\n  {', '.join(sorted(search_engines.keys()))} \n")

    # Add alias for 'auto' if it exists
    if "auto" in search_engines and "meta" not in search_engines:
        search_engines["meta"] = search_engines["auto"]

    # Register local document collections
    local_collections_data = _get_setting(
        "search.engine.local",
        {},
        db_session=db_session,
        settings_snapshot=settings_snapshot,
        username=username,
    )
    local_collections_data = _extract_per_engine_config(local_collections_data)

    for collection, config in local_collections_data.items():
        if not config.get("enabled", True):
            # Search engine is not enabled. Ignore.
            logger.info(f"Ignoring disabled local collection '{collection}'.")
            continue

        if "paths" in config and isinstance(config["paths"], str):
            # This will be saved as a json array.
            try:
                config["paths"] = json.loads(config["paths"])
            except json.decoder.JSONDecodeError:
                logger.exception(
                    f"Path for local collection '{collection}' is not a valid JSON array: "
                    f"{config['paths']}"
                )
                config["paths"] = []

        # Create a new dictionary with required search engine fields
        engine_config = {
            "default_params": config,
            "requires_llm": True,
        }
        engine_config_prefix = f"search.engine.local.{collection}"
        engine_config["module_path"] = _get_setting(
            f"{engine_config_prefix}.module_path",
            "local_deep_research.web_search_engines.engines.search_engine_local",
            db_session=db_session,
            settings_snapshot=settings_snapshot,
            username=username,
        )
        engine_config["class_name"] = _get_setting(
            f"{engine_config_prefix}.class_name",
            "LocalSearchEngine",
            db_session=db_session,
            settings_snapshot=settings_snapshot,
            username=username,
        )

        # Copy these specific fields to the top level if they exist
        for field in ["strengths", "weaknesses", "reliability", "description"]:
            if field in config:
                engine_config[field] = config[field]

        search_engines[collection] = engine_config

    logger.info("Registered local document collections as search engines")

    # Register Library RAG as a search engine
    library_enabled = _get_setting(
        "search.engine.library.enabled",
        True,
        db_session=db_session,
        settings_snapshot=settings_snapshot,
        username=username,
    )

    if library_enabled:
        search_engines["library"] = {
            "module_path": "local_deep_research.web_search_engines.engines.search_engine_library",
            "class_name": "LibraryRAGSearchEngine",
            "requires_llm": True,
            "display_name": "Search All Collections",
            "default_params": {},
            "description": "Search across all your document collections using semantic search",
            "strengths": [
                "Searches all your curated collections of research papers and documents",
                "Uses semantic search for better relevance",
                "Returns documents you've already saved and reviewed",
            ],
            "weaknesses": [
                "Limited to documents already in your collections",
                "Requires documents to be indexed first",
            ],
            "reliability": "High - searches all your collections",
        }
        logger.info("Registered Library RAG as search engine")

    # Register document collections as individual search engines
    if library_enabled:
        try:
            from ..database.models.library import Collection
            from ..database.session_context import get_user_db_session

            # Get username from settings_snapshot if available
            collection_username = (
                settings_snapshot.get("_username")
                if settings_snapshot
                else username
            )

            if collection_username:
                with get_user_db_session(collection_username) as session:
                    collections = session.query(Collection).all()

                    for collection in collections:
                        engine_id = f"collection_{collection.id}"
                        # Add suffix to distinguish from the all-collections search
                        display_name = f"{collection.name} (Collection)"
                        search_engines[engine_id] = {
                            "module_path": "local_deep_research.web_search_engines.engines.search_engine_collection",
                            "class_name": "CollectionSearchEngine",
                            "requires_llm": True,
                            "is_local": True,
                            "display_name": display_name,
                            "default_params": {
                                "collection_id": collection.id,
                                "collection_name": collection.name,
                            },
                            "description": (
                                collection.description
                                if collection.description
                                else f"Search documents in {collection.name} collection only"
                            ),
                            "strengths": [
                                f"Searches only documents in {collection.name}",
                                "Focused semantic search within specific topic area",
                                "Returns documents from a curated collection",
                            ],
                            "weaknesses": [
                                "Limited to documents in this collection",
                                "Smaller result pool than full library search",
                            ],
                            "reliability": "High - searches a specific collection",
                        }

                    logger.info(
                        f"Registered {len(collections)} document collections as search engines"
                    )
            else:
                logger.debug(
                    "No username available for collection registration"
                )
        except Exception as e:
            logger.warning(f"Could not register document collections: {e}")

    return search_engines


def default_search_engine(
    username: Optional[str] = None,
    db_session: Optional[Session] = None,
    settings_snapshot: Optional[Dict[str, Any]] = None,
) -> str:
    """
    Returns the configured default search engine.

    Args:
        username: Username for backward compatibility (deprecated)
        db_session: Database session for direct access (preferred for web routes)
        settings_snapshot: Settings snapshot for thread context (preferred for background threads)

    Returns:
        The configured default search engine.
    """
    return _get_setting(
        "search.engine.DEFAULT_SEARCH_ENGINE",
        "wikipedia",
        db_session=db_session,
        settings_snapshot=settings_snapshot,
        username=username,
    )


def local_search_engines(
    username: Optional[str] = None,
    db_session: Optional[Session] = None,
    settings_snapshot: Optional[Dict[str, Any]] = None,
) -> List[str]:
    """
    Returns a list of the enabled local search engines.

    Args:
        username: Username for backward compatibility (deprecated)
        db_session: Database session for direct access (preferred for web routes)
        settings_snapshot: Settings snapshot for thread context (preferred for background threads)

    Returns:
        A list of the enabled local search engines.
    """
    local_collections_data = _get_setting(
        "search.engine.local",
        {},
        db_session=db_session,
        settings_snapshot=settings_snapshot,
        username=username,
    )
    local_collections_data = _extract_per_engine_config(local_collections_data)

    # Don't include the `local_all` collection.
    local_collections_data.pop("local_all", None)
    # Remove disabled collections.
    local_collections_data = {
        k: v
        for k, v in local_collections_data.items()
        if v.get("enabled", True)
    }

    enabled_collections = list(local_collections_data.keys())
    logger.debug(f"Using local collections: {enabled_collections}")
    return enabled_collections
