"""
Cross-engine search result filter implementation.
"""

from typing import Dict, List

from loguru import logger

from ...utilities.json_utils import extract_json, get_llm_response_text
from .base_filter import BaseFilter


class CrossEngineFilter(BaseFilter):
    """Filter that ranks and filters results from multiple search engines."""

    def __init__(
        self,
        model,
        max_results=None,
        default_reorder=True,
        default_reindex=True,
        settings_snapshot=None,
    ):
        """
        Initialize the cross-engine filter.

        Args:
            model: Language model to use for relevance assessment
            max_results: Maximum number of results to keep after filtering
            default_reorder: Default setting for reordering results by relevance
            default_reindex: Default setting for reindexing results after filtering
            settings_snapshot: Settings snapshot for thread context
        """
        super().__init__(model)
        # Import from thread_settings to avoid database dependencies
        from ...config.thread_settings import (
            get_setting_from_snapshot,
            NoSettingsContextError,
        )

        # Get max_results from database settings if not provided
        if max_results is None:
            try:
                max_results = get_setting_from_snapshot(
                    "search.cross_engine_max_results",
                    default=100,
                    settings_snapshot=settings_snapshot,
                )
                if max_results is not None:
                    max_results = int(max_results)
                else:
                    max_results = 100
            except (NoSettingsContextError, TypeError, ValueError):
                max_results = 100
        self.max_results = max_results

        # Max number of result previews shown to the LLM for relevance ranking.
        # Higher values let the LLM evaluate more candidates but increase prompt
        # size and latency.
        try:
            self.max_context_items = int(
                get_setting_from_snapshot(
                    "search.cross_engine_max_context_items",
                    default=30,
                    settings_snapshot=settings_snapshot,
                )
            )
        except (NoSettingsContextError, TypeError, ValueError):
            self.max_context_items = 30

        self.default_reorder = default_reorder
        self.default_reindex = default_reindex

    def _prepare_and_return(self, results, *, reindex, start_index):
        """Optionally reindex results and return them."""
        if reindex:
            for i, result in enumerate(results):
                result["index"] = str(i + start_index + 1)
        return results

    def _valid_unique_indices(self, ranked_indices, upper_bound):
        """Yield valid indices once, preserving first-seen order."""
        seen = set()
        for idx in ranked_indices:
            if not isinstance(idx, int) or isinstance(idx, bool):
                logger.warning(
                    f"Skipping non-integer ranked index from cross-engine filter: {idx!r}"
                )
                continue
            if idx in seen:
                continue
            if 0 <= idx < upper_bound:
                seen.add(idx)
                yield idx

    def filter_results(
        self,
        results: List[Dict],
        query: str,
        reorder=None,
        reindex=None,
        start_index=0,
        **kwargs,
    ) -> List[Dict]:
        """
        Filter and rank search results from multiple engines by relevance.

        Args:
            results: Combined list of search results from all engines
            query: The original search query
            reorder: Whether to reorder results by relevance (default: use instance default)
            reindex: Whether to update result indices after filtering (default: use instance default)
            start_index: Starting index for the results (used for continuous indexing)
            **kwargs: Additional parameters

        Returns:
            Filtered list of search results
        """
        # Use instance defaults if not specified
        if reorder is None:
            reorder = self.default_reorder
        if reindex is None:
            reindex = self.default_reindex

        if not self.model or len(results) <= 10:  # Don't filter if few results
            return self._prepare_and_return(
                results[: min(self.max_results, len(results))],
                reindex=reindex,
                start_index=start_index,
            )

        max_context_items = min(self.max_context_items, len(results))
        context_results = results[:max_context_items]

        # Create context for LLM
        preview_context = []
        for i, result in enumerate(context_results):
            title = result.get("title", "Untitled").strip()
            snippet = result.get("snippet", "").strip()
            engine = result.get("engine", "Unknown engine")

            # Clean up snippet if too long
            if len(snippet) > 200:
                snippet = snippet[:200] + "..."

            preview_context.append(
                f"[{i}] Engine: {engine} | Title: {title}\nSnippet: {snippet}"
            )

        context = "\n\n".join(preview_context)

        prompt = f"""You are a search result filter. Your task is to rank search results from multiple engines by relevance to a query.

Query: "{query}"

Search Results:
{context}

Return the search results as a JSON array of indices, ranked from most to least relevant to the query.
Only include indices of results that are actually relevant to the query.
For example: [3, 0, 7, 1]

If no results seem relevant to the query, return an empty array: []"""

        try:
            # Get LLM's evaluation
            response = self.model.invoke(prompt)
            response_text = get_llm_response_text(response)
            ranked_indices = extract_json(response_text, expected_type=list)

            if ranked_indices is not None:
                # If not reordering, just filter based on the indices
                if not reorder:
                    # Just keep the results that were deemed relevant
                    filtered_results = []
                    for idx in sorted(
                        self._valid_unique_indices(
                            ranked_indices, len(context_results)
                        )
                    ):  # Sort to maintain original order
                        filtered_results.append(context_results[idx])

                    # Limit results if needed
                    final_results = filtered_results[
                        : min(self.max_results, len(filtered_results))
                    ]

                    if not final_results and results:
                        logger.info(
                            "Cross-engine filtering removed all "
                            "results, returning top 10 originals"
                        )
                        return self._prepare_and_return(
                            context_results[: min(10, len(context_results))],
                            reindex=reindex,
                            start_index=start_index,
                        )

                    logger.info(
                        f"Cross-engine filtering kept {len(final_results)} out of {len(results)} results without reordering"
                    )
                    return self._prepare_and_return(
                        final_results,
                        reindex=reindex,
                        start_index=start_index,
                    )

                # Create ranked results list (reordering)
                ranked_results = []
                for idx in self._valid_unique_indices(
                    ranked_indices, len(context_results)
                ):
                    ranked_results.append(context_results[idx])

                # If filtering removed everything, return top results
                if not ranked_results and results:
                    logger.info(
                        "Cross-engine filtering removed all results, returning top 10 originals instead"
                    )
                    return self._prepare_and_return(
                        context_results[: min(10, len(context_results))],
                        reindex=reindex,
                        start_index=start_index,
                    )

                # Limit results if needed
                max_filtered = min(self.max_results, len(ranked_results))
                final_results = ranked_results[:max_filtered]

                logger.info(
                    f"Cross-engine filtering kept {len(final_results)} out of {len(results)} results with reordering={reorder}, reindex={reindex}"
                )
                return self._prepare_and_return(
                    final_results,
                    reindex=reindex,
                    start_index=start_index,
                )
            logger.info(
                "Could not find JSON array in response, returning original results"
            )
            return self._prepare_and_return(
                context_results[: min(self.max_results, len(context_results))],
                reindex=reindex,
                start_index=start_index,
            )

        except Exception:
            logger.exception("Cross-engine filtering error")
            return self._prepare_and_return(
                context_results[: min(self.max_results, len(context_results))],
                reindex=reindex,
                start_index=start_index,
            )
