import json
from typing import Dict, List, Union, cast

from loguru import logger

from ...citation_handler import CitationHandler

# Model and search should be provided by AdvancedSearchSystem
from ...config.thread_settings import get_setting_from_snapshot
from ...utilities.enums import KnowledgeAccumulationApproach
from ...utilities.search_utilities import extract_links_from_search_results
from ..findings.repository import FindingsRepository
from ..knowledge.standard_knowledge import StandardKnowledge
from ..questions.standard_question import StandardQuestionGenerator
from .base_strategy import BaseSearchStrategy


class StandardSearchStrategy(BaseSearchStrategy):
    """Standard iterative search strategy that generates follow-up questions."""

    def __init__(
        self,
        search,
        model,
        citation_handler=None,
        all_links_of_system=None,
        settings_snapshot=None,
    ):
        """Initialize with optional dependency injection for testing."""
        super().__init__(
            all_links_of_system=all_links_of_system,
            settings_snapshot=settings_snapshot,
        )
        self.search = search
        self.model = model

        # Get iterations setting
        self.max_iterations = int(
            get_setting_from_snapshot(
                "search.iterations",
                settings_snapshot=settings_snapshot,
                default=3,
            )
        )

        self.questions_per_iteration = int(
            get_setting_from_snapshot(
                "search.questions_per_iteration",
                settings_snapshot=settings_snapshot,
                default=1,
            )
        )
        self.context_limit = int(
            get_setting_from_snapshot(
                "general.knowledge_accumulation_context_limit",
                settings_snapshot=settings_snapshot,
                default=2000000,
            )
        )
        # Note: questions_by_iteration is already initialized by parent class

        # Use provided citation_handler or create one
        self.citation_handler = citation_handler or CitationHandler(
            self.model, settings_snapshot=settings_snapshot
        )

        # Initialize specialized components
        self.question_generator = StandardQuestionGenerator(self.model)
        self.knowledge_generator = StandardKnowledge(self.model)
        self.findings_repository = FindingsRepository(self.model)

        # Initialize other attributes
        self.progress_callback = None

    def analyze_topic(self, query: str) -> Dict:
        """Standard implementation of the topic analysis process."""
        logger.info(f"Starting research on topic: {query}")

        findings = []
        current_knowledge = ""
        iteration = 0
        total_iterations = self.max_iterations
        section_links = []

        self._update_progress(
            "Initializing research system",
            5,
            {"phase": "init", "iterations_planned": total_iterations},
        )

        # Check if search engine is available
        if self.search is None:
            error_msg = "Error: No search engine available. Please check your configuration."
            self._update_progress(
                error_msg,
                100,
                {
                    "phase": "error",
                    "error": "No search engine available",
                    "status": "failed",
                },
            )
            return {
                "findings": [],
                "iterations": 0,
                "questions": {},
                "formatted_findings": "Error: Unable to conduct research without a search engine.",
                "current_knowledge": "",
                "error": error_msg,
            }

        while iteration < self.max_iterations:
            iteration_progress_base = (iteration / total_iterations) * 100
            self._update_progress(
                f"Starting iteration {iteration + 1} of {total_iterations}",
                int(iteration_progress_base),
                {"phase": "iteration_start", "iteration": iteration + 1},
            )

            # Generate questions for this iteration using the question generator
            # Prepare context for question generation
            context = f"""Current Query: {query}
Current Knowledge: {current_knowledge}
Previous Questions: {json.dumps(self.questions_by_iteration, indent=2)}
Iteration: {iteration + 1} of {total_iterations}"""

            # Call question generator with updated interface
            questions = self.question_generator.generate_questions(
                query=query,
                current_knowledge=context,
                questions_per_iteration=self.questions_per_iteration,
                questions_by_iteration=self.questions_by_iteration,
            )

            self.questions_by_iteration[iteration] = questions
            logger.info(f"Generated questions: {questions}")

            question_count = len(questions)
            knowledge_accumulation = get_setting_from_snapshot(
                "general.knowledge_accumulation",
                "ITERATION",
                settings_snapshot=self.settings_snapshot,
            )
            for q_idx, question in enumerate(questions):
                question_progress_base = iteration_progress_base + (
                    ((q_idx + 1) / question_count)
                    * (100 / total_iterations)
                    * 0.5
                )

                self._update_progress(
                    f"Searching for: {question}",
                    int(question_progress_base),
                    {
                        "phase": "search",
                        "iteration": iteration + 1,
                        "question_index": q_idx + 1,
                    },
                )

                try:
                    if self.search is None:
                        self._update_progress(
                            f"Search engine unavailable, skipping search for: {question}",
                            int(question_progress_base + 2),
                            {
                                "phase": "search_error",
                                "error": "No search engine available",
                            },
                        )
                        search_results = []
                    else:
                        search_results = self.search.run(question)
                except Exception as e:
                    error_msg = f"Error during search: {e!s}"
                    logger.exception(f"SEARCH ERROR: {error_msg}")
                    self._handle_search_error(
                        e, question, int(question_progress_base + 10)
                    )
                    search_results = []

                if search_results is None:
                    self._update_progress(
                        f"No search results found for question: {question}",
                        int(question_progress_base + 2),
                        {"phase": "search_complete", "result_count": 0},
                    )
                    search_results = []  # Initialize to empty list instead of None
                    continue

                self._update_progress(
                    f"Found {len(search_results)} results for question: {question}",
                    int(question_progress_base + 2),
                    {
                        "phase": "search_complete",
                        "result_count": len(search_results),
                    },
                )

                logger.info(f"len search: {len(search_results)}")

                if len(search_results) == 0:
                    continue

                self._update_progress(
                    f"Analyzing results for: {question}",
                    int(question_progress_base + 5),
                    {"phase": "analysis"},
                )

                try:
                    result = self.citation_handler.analyze_followup(
                        question,
                        search_results,
                        current_knowledge,
                        nr_of_links=len(self.all_links_of_system),
                    )
                    links = extract_links_from_search_results(search_results)
                    self.all_links_of_system.extend(links)
                    section_links.extend(links)

                    if result is not None:
                        results_with_links = str(result["content"])
                        findings.append(
                            {
                                "phase": f"Follow-up {iteration}.{questions.index(question) + 1}",
                                "content": results_with_links,
                                "question": question,
                                "search_results": search_results,
                                "documents": result["documents"],
                            }
                        )

                        if knowledge_accumulation != str(
                            KnowledgeAccumulationApproach.NO_KNOWLEDGE.value
                        ):
                            current_knowledge = (
                                current_knowledge
                                + "\n\n\n New: \n"
                                + results_with_links
                            )

                        if knowledge_accumulation == str(
                            KnowledgeAccumulationApproach.QUESTION.value
                        ):
                            logger.info("Compressing knowledge")
                            self._update_progress(
                                f"Compress Knowledge for: {question}",
                                int(question_progress_base + 0),
                                {"phase": "analysis"},
                            )
                            current_knowledge = (
                                self.knowledge_generator.compress_knowledge(
                                    current_knowledge, query, section_links
                                )
                            )

                        self._update_progress(
                            f"Analysis complete for question: {question}",
                            int(question_progress_base + 10),
                            {"phase": "analysis_complete"},
                        )
                except Exception as e:
                    error_msg = f"Error analyzing results: {e!s}"
                    logger.exception(f"ANALYSIS ERROR: {error_msg}")
                    self._handle_analysis_error(
                        e, question, int(question_progress_base + 10)
                    )

            iteration += 1

            self._update_progress(
                f"Compressing knowledge after iteration {iteration}",
                int((iteration / total_iterations) * 100 - 5),
                {"phase": "knowledge_compression"},
            )

            if (
                knowledge_accumulation
                == KnowledgeAccumulationApproach.ITERATION.value
            ):
                try:
                    logger.info("ITERATION - Compressing Knowledge")
                    current_knowledge = (
                        self.knowledge_generator.compress_knowledge(
                            current_knowledge, query, section_links
                        )
                    )
                    logger.info("FINISHED ITERATION - Compressing Knowledge")
                except Exception as e:
                    error_msg = f"Error compressing knowledge: {e!s}"
                    logger.exception(f"COMPRESSION ERROR: {error_msg}")
                    self._handle_search_error(
                        e, query, int((iteration / total_iterations) * 100 - 3)
                    )

            self._update_progress(
                f"Iteration {iteration} complete",
                int((iteration / total_iterations) * 100),
                {"phase": "iteration_complete", "iteration": iteration},
            )

            # Extract content from findings for synthesis
            finding_contents = [
                f["content"] for f in findings if "content" in f
            ]

            # First synthesize findings to get coherent content
            synthesized_content = self.findings_repository.synthesize_findings(
                query,
                finding_contents,
                cast(
                    List[Union[Dict, str]], findings
                ),  # Pass the full findings list with search results
                accumulated_knowledge=current_knowledge,
                old_formatting=False,  # Don't format here, just synthesize content
            )

            # Transfer questions to the repository
            self.findings_repository.set_questions_by_iteration(
                self.questions_by_iteration
            )

            # Now format the findings with search questions and sources
            formatted_findings = (
                self.findings_repository.format_findings_to_text(
                    findings, synthesized_content
                )
            )

            # Add the synthesized content to the repository
            self.findings_repository.add_finding(query, synthesized_content)

        self._update_progress("Research complete", 95, {"phase": "complete"})

        return {
            "findings": findings,
            "iterations": iteration,
            "questions": self.questions_by_iteration,
            "formatted_findings": formatted_findings,
            "current_knowledge": current_knowledge,
        }
