"""
Extended tests for FindingsRepository - Research findings management.

Tests cover:
- Repository initialization
- Finding addition and retrieval
- Document management
- Synthesis functionality
- Error handling and edge cases
"""


class TestFindingsRepositoryInitialization:
    """Tests for FindingsRepository initialization."""

    def test_empty_findings_on_init(self):
        """Findings should be empty on initialization."""
        findings = {}
        assert len(findings) == 0

    def test_empty_documents_on_init(self):
        """Documents list should be empty on initialization."""
        documents = []
        assert len(documents) == 0

    def test_empty_questions_by_iteration(self):
        """Questions by iteration should be empty on init."""
        questions_by_iteration = {}
        assert len(questions_by_iteration) == 0


class TestFindingAddition:
    """Tests for adding findings."""

    def test_add_string_finding(self):
        """Should convert string finding to dict."""
        findings = {}
        query = "test query"

        findings.setdefault(query, [])

        finding = "This is a finding"
        finding_dict = {
            "phase": "Synthesis",
            "content": finding,
            "question": query,
            "search_results": [],
            "documents": [],
        }
        findings[query].append(finding_dict)

        assert len(findings[query]) == 1
        assert findings[query][0]["content"] == "This is a finding"

    def test_add_dict_finding(self):
        """Should add dict finding directly."""
        findings = {}
        query = "test query"

        findings.setdefault(query, [])

        finding = {
            "phase": "Analysis",
            "content": "Analysis content",
            "question": query,
        }
        findings[query].append(finding)

        assert findings[query][0]["phase"] == "Analysis"

    def test_final_synthesis_creates_extra_entry(self):
        """Final synthesis should create extra synthesis entry."""
        findings = {}
        query = "test query"

        findings.setdefault(query, [])

        finding = {
            "phase": "Final synthesis",
            "content": "Final content",
        }
        findings[query].append(finding)

        if finding.get("phase") == "Final synthesis":
            findings[query + "_synthesis"] = [
                {
                    "phase": "Synthesis",
                    "content": finding.get("content", ""),
                    "question": query,
                    "search_results": [],
                    "documents": [],
                }
            ]

        assert query + "_synthesis" in findings
        assert findings[query + "_synthesis"][0]["content"] == "Final content"

    def test_multiple_findings_same_query(self):
        """Should support multiple findings for same query."""
        findings = {}
        query = "test query"

        findings.setdefault(query, [])

        for i in range(3):
            findings[query].append({"content": f"Finding {i}"})

        assert len(findings[query]) == 3


class TestFindingRetrieval:
    """Tests for retrieving findings."""

    def test_get_findings_existing_query(self):
        """Should return findings for existing query."""
        findings = {"query1": [{"content": "content1"}]}

        result = findings.get("query1", [])

        assert len(result) == 1
        assert result[0]["content"] == "content1"

    def test_get_findings_nonexistent_query(self):
        """Should return empty list for nonexistent query."""
        findings = {"query1": [{"content": "content1"}]}

        result = findings.get("query2", [])

        assert result == []


class TestFindingClearing:
    """Tests for clearing findings."""

    def test_clear_findings_removes_query(self):
        """Should remove findings for specific query."""
        findings = {
            "query1": [{"content": "content1"}],
            "query2": [{"content": "content2"}],
        }

        if "query1" in findings:
            del findings["query1"]

        assert "query1" not in findings
        assert "query2" in findings

    def test_clear_nonexistent_query(self):
        """Should handle clearing nonexistent query."""
        findings = {"query1": [{"content": "content1"}]}

        if "query2" in findings:
            del findings["query2"]

        # Should not raise error
        assert "query1" in findings


class TestDocumentManagement:
    """Tests for document management."""

    def test_add_documents(self):
        """Should add documents to repository."""
        documents = []
        new_docs = ["doc1", "doc2", "doc3"]

        documents.extend(new_docs)

        assert len(documents) == 3

    def test_documents_accumulate(self):
        """Documents should accumulate across additions."""
        documents = ["existing"]
        new_docs = ["new1", "new2"]

        documents.extend(new_docs)

        assert len(documents) == 3


class TestQuestionsManagement:
    """Tests for questions by iteration management."""

    def test_set_questions_by_iteration(self):
        """Should set questions by iteration."""
        questions_by_iteration = {}
        new_questions = {
            1: ["Q1", "Q2"],
            2: ["Q3", "Q4"],
        }

        questions_by_iteration = new_questions.copy()

        assert len(questions_by_iteration) == 2
        assert questions_by_iteration[1] == ["Q1", "Q2"]

    def test_questions_copy_not_reference(self):
        """Should copy questions, not reference."""
        new_questions = {1: ["Q1"]}
        questions_by_iteration = new_questions.copy()

        new_questions[1].append("Q2")

        # Original should be modified, copy should not
        assert len(new_questions[1]) == 2
        # Note: shallow copy - inner list is still referenced
        # The copy shares the same inner list reference
        assert len(questions_by_iteration[1]) == 2


class TestSynthesisFormatting:
    """Tests for synthesis formatting."""

    def test_format_findings_to_text_structure(self):
        """Should format findings into text output."""
        findings_list = [
            {"phase": "Phase 1", "content": "Content 1"},
            {"phase": "Phase 2", "content": "Content 2"},
        ]
        synthesized_content = "Final synthesis content"

        # Simplified formatting
        output = f"Synthesis:\n{synthesized_content}\n\nFindings:\n"
        for f in findings_list:
            output += f"- {f['phase']}: {f['content']}\n"

        assert "Final synthesis content" in output
        assert "Phase 1" in output

    def test_format_empty_findings(self):
        """Should handle empty findings list."""
        findings_list = []
        synthesized_content = "Only synthesis"

        output = f"Synthesis:\n{synthesized_content}\n\n"
        if not findings_list:
            output += "No detailed findings."

        assert "No detailed findings" in output


class TestSynthesisGeneration:
    """Tests for synthesis generation."""

    def test_synthesize_findings_prompt_structure(self):
        """Synthesis prompt should have correct structure."""
        query = "test query"
        sub_queries = ["sub1", "sub2"]
        current_knowledge = "Accumulated knowledge"

        prompt = f"""Original Query: {query}

Accumulated Knowledge:
{current_knowledge}

Sub-questions asked (for context):
{chr(10).join(f"- {sq}" for sq in sub_queries)}
"""

        assert "test query" in prompt
        assert "Accumulated knowledge" in prompt
        assert "- sub1" in prompt
        assert "- sub2" in prompt

    def test_extract_finding_content(self):
        """Should extract content from findings."""
        findings = [
            {"content": "Content 1"},
            "String finding",
            {"content": "Content 2"},
        ]

        finding_texts = []
        for item in findings:
            if isinstance(item, dict) and "content" in item:
                finding_texts.append(item["content"])
            elif isinstance(item, str):
                finding_texts.append(item)

        assert len(finding_texts) == 3
        assert finding_texts[1] == "String finding"

    def test_knowledge_truncation_threshold(self):
        """Should truncate knowledge exceeding threshold."""
        current_knowledge = "x" * 30000
        max_chars = 24000

        if len(current_knowledge) > max_chars:
            first_part = current_knowledge[:12000]
            last_part = current_knowledge[-12000:]
            truncated = (
                f"{first_part}\n\n[...content truncated...]\n\n{last_part}"
            )
        else:
            truncated = current_knowledge

        assert "[...content truncated...]" in truncated
        assert len(truncated) < len(current_knowledge)


class TestErrorTypeDetection:
    """Tests for error type detection in synthesis."""

    def test_detect_timeout_error(self):
        """Should detect timeout errors."""
        error_message = "Connection timed out after 30 seconds"

        error_type = "unknown"
        if (
            "timeout" in error_message.lower()
            or "timed out" in error_message.lower()
        ):
            error_type = "timeout"

        assert error_type == "timeout"

    def test_detect_token_limit_error(self):
        """Should detect token limit errors."""
        error_message = "Request exceeds context length limit"

        error_type = "unknown"
        if (
            "context length" in error_message.lower()
            or "token limit" in error_message.lower()
        ):
            error_type = "token_limit"

        assert error_type == "token_limit"

    def test_detect_rate_limit_error(self):
        """Should detect rate limit errors."""
        error_message = "API rate limit exceeded"

        error_type = "unknown"
        if (
            "rate limit" in error_message.lower()
            or "rate_limit" in error_message.lower()
        ):
            error_type = "rate_limit"

        assert error_type == "rate_limit"

    def test_detect_connection_error(self):
        """Should detect connection errors."""
        error_message = "Connection refused by server"

        error_type = "unknown"
        if (
            "connection" in error_message.lower()
            or "network" in error_message.lower()
        ):
            error_type = "connection"

        assert error_type == "connection"

    def test_detect_auth_error(self):
        """Should detect authentication errors."""
        error_message = "Invalid API key provided"

        error_type = "unknown"
        if (
            "api key" in error_message.lower()
            or "authentication" in error_message.lower()
        ):
            error_type = "authentication"

        assert error_type == "authentication"


class TestLinkFormatting:
    """Tests for link formatting."""

    def test_format_links_basic(self):
        """Should format links correctly."""
        links = [
            {"title": "Link 1", "url": "http://example1.com"},
            {"title": "Link 2", "url": "http://example2.com"},
        ]

        formatted = "\n".join(
            f"{i + 1}. {link['title']}\n   URL: {link['url']}"
            for i, link in enumerate(links)
        )

        assert "1. Link 1" in formatted
        assert "URL: http://example1.com" in formatted

    def test_format_empty_links(self):
        """Should handle empty links list."""
        links = []

        if not links:
            formatted = ""
        else:
            formatted = "Some links"

        assert formatted == ""


class TestOldFormattingPath:
    """Tests for old formatting path."""

    def test_convert_string_findings_to_dicts(self):
        """Should convert string findings for old formatting."""
        findings = ["Finding 1", "Finding 2", {"phase": "P3", "content": "C3"}]

        findings_list = []
        for i, item in enumerate(findings):
            if isinstance(item, str):
                findings_list.append(
                    {"phase": f"Finding {i + 1}", "content": item}
                )
            elif isinstance(item, dict):
                findings_list.append(item)

        assert len(findings_list) == 3
        assert findings_list[0]["phase"] == "Finding 1"
        assert findings_list[2]["phase"] == "P3"


class TestTokenEstimation:
    """Tests for token estimation."""

    def test_estimate_tokens_from_chars(self):
        """Should estimate tokens from character count."""
        text = "x" * 4000
        chars_per_token = 4

        estimated_tokens = len(text) / chars_per_token

        assert estimated_tokens == 1000

    def test_check_token_limit_exceeded(self):
        """Should detect when token limit exceeded."""
        text = "x" * 60000  # ~15000 tokens
        max_safe_tokens = 12000
        chars_per_token = 4

        estimated_tokens = len(text) / chars_per_token
        exceeded = estimated_tokens > max_safe_tokens

        assert exceeded is True
