"""Tests for the topic clustering analyzer."""

import pytest

# Skip all tests if sklearn not installed
sklearn = pytest.importorskip("sklearn")

from turboseo.analyzers.topic_clustering import (
    LSIKeywords,
    TopicCluster,
    TopicClusteringResult,
    analyze_topics,
)


class TestTopicClustering:
    """Tests for topic clustering analysis."""

    def test_analyzes_multi_section_content(self):
        """Test clustering works with multiple sections."""
        content = """
# Main Title

Introduction text here.

## Section One

This section discusses technology and innovation in the digital age.
Artificial intelligence is transforming industries around the world.
Machine learning models help automate complex decision processes.

## Section Two

This section covers business strategies and market analysis topics.
Companies must adapt to changing consumer behavior and preferences.
Strategic planning is essential for sustainable business growth.

## Section Three

More technology content about software development practices here.
Programming languages evolve to meet new development requirements.
Cloud computing enables scalable infrastructure solutions today.
"""
        result = analyze_topics(content)
        assert result.section_count >= 2
        assert result.cluster_count >= 1
        assert len(result.clusters) >= 1

    def test_extracts_lsi_keywords(self):
        """Test LSI keyword extraction."""
        content = """
# SEO Guide

Introduction to search engine optimization techniques.

## On-Page SEO

On-page SEO involves optimizing content and HTML source code.
Keywords should be placed naturally throughout the content.
Meta descriptions help improve click-through rates from search.

## Off-Page SEO

Off-page SEO focuses on building backlinks from other sites.
Link building strategies require quality content creation.
Social signals can also influence search engine rankings today.

## Technical SEO

Technical SEO ensures search engines can crawl your site.
Site speed and mobile optimization are critical factors.
Structured data helps search engines understand your content.
"""
        result = analyze_topics(content)
        assert len(result.lsi_keywords.single_words) > 0
        assert isinstance(result.lsi_keywords.phrases, list)

    def test_cluster_count_formula(self):
        """Test cluster count is min(5, max(2, sections//2))."""
        # Create content with 6 sections -> should get 3 clusters
        sections = []
        for i in range(6):
            sections.append(f"## Section {i}\n\n" + " ".join(["word"] * 25))

        content = "# Title\n\n" + "\n\n".join(sections)
        result = analyze_topics(content)

        # 6 sections -> max(2, 6//2) = 3, min(5, 3) = 3
        assert result.cluster_count >= 2
        assert result.cluster_count <= 5

    def test_too_few_sections_handled(self):
        """Test graceful handling when <2 sections available."""
        content = """
# Only Title

This is a single paragraph without any H2 sections defined.
It should not crash but return empty clusters gracefully.
"""
        result = analyze_topics(content)
        assert result.section_count < 2
        assert result.cluster_count == 0
        assert len(result.clusters) == 0
        assert len(result.recommendations) > 0

    def test_single_section_not_enough(self):
        """Test that a single section returns empty clustering."""
        content = """
# Title

## Only Section

This is the only section with enough words to qualify for analysis.
It should be detected but clustering requires at least two sections.
Additional text to meet the minimum word count requirement here.
"""
        result = analyze_topics(content)
        # Single section found, but not enough for clustering
        assert result.cluster_count == 0


class TestTopicCluster:
    """Tests for TopicCluster model."""

    def test_cluster_has_required_fields(self):
        """Test cluster has all required fields."""
        content = """
# Title

## Section A

Content about topic A with technology and innovation keywords.
Artificial intelligence transforms how businesses operate today.
Machine learning enables predictive analytics capabilities now.

## Section B

Content about topic B with business and strategy keywords here.
Strategic planning helps companies achieve sustainable growth.
Market analysis provides insights for better decision making.
"""
        result = analyze_topics(content)
        if result.clusters:
            cluster = result.clusters[0]
            assert hasattr(cluster, "cluster_id")
            assert hasattr(cluster, "top_terms")
            assert hasattr(cluster, "section_indices")
            assert hasattr(cluster, "section_count")
            assert isinstance(cluster.top_terms, list)
            assert isinstance(cluster.section_indices, list)


class TestLSIKeywords:
    """Tests for LSI keyword extraction."""

    def test_single_words_extracted(self):
        """Test single-word keywords are extracted."""
        content = """
# Technology Guide

Introduction to technology concepts.

## Software Development

Software engineering involves designing and building applications.
Programming requires understanding of algorithms and data structures.
Testing ensures software quality and reliability for end users.

## Cloud Computing

Cloud platforms provide scalable infrastructure services today.
Containerization helps with deployment and resource management.
Microservices architecture enables modular application design.
"""
        result = analyze_topics(content)
        # Should extract single words
        assert isinstance(result.lsi_keywords.single_words, list)
        # Should filter stop words
        for word in result.lsi_keywords.single_words:
            assert word.lower() not in {"the", "a", "is", "and", "to"}

    def test_phrases_extracted(self):
        """Test multi-word phrases are extracted."""
        content = """
# SEO Strategy

Introduction to search engine optimization strategy basics.

## Keyword Research

Keyword research helps identify target search terms today.
Long tail keywords often have lower competition levels.
Search volume analysis guides content strategy decisions.

## Content Optimization

Content optimization improves search engine visibility now.
On page SEO factors include title tags and meta descriptions.
Internal linking helps distribute page authority throughout site.
"""
        result = analyze_topics(content)
        # Phrases should be multi-word
        for phrase in result.lsi_keywords.phrases:
            if phrase:  # If any phrases found
                assert " " in phrase


class TestScrubResult:
    """Tests for TopicClusteringResult model."""

    def test_result_is_pydantic_model(self):
        """Test result is a proper Pydantic model."""
        content = """
# Title

## Section One

Content here with enough words to qualify for analysis today.
Additional content to meet minimum word requirements now.
More content to ensure we have sufficient text volume.

## Section Two

Different content in this section about another topic.
This section discusses different subjects and themes.
More words to meet the minimum requirements here.
"""
        result = analyze_topics(content)
        assert isinstance(result, TopicClusteringResult)

    def test_result_has_all_fields(self):
        """Test result has all required fields."""
        content = """
# Title

## Section One

Twenty words of content here to meet minimum requirements today now.
Additional words to ensure we reach the word count threshold.

## Section Two

Another twenty words of content in this second section today.
More words added here to meet the minimum word count.
"""
        result = analyze_topics(content)
        assert hasattr(result, "section_count")
        assert hasattr(result, "cluster_count")
        assert hasattr(result, "clusters")
        assert hasattr(result, "lsi_keywords")
        assert hasattr(result, "recommendations")

    def test_result_serializable(self):
        """Test result can be serialized to dict."""
        content = """
# Title

## Section A

Content about topic A with sufficient words for analysis here.
Additional content to meet the minimum word count requirement.
More text added to ensure we have enough words today.

## Section B

Content about topic B with different subject matter here.
More content to meet minimum requirements in section B.
Additional words for this section to qualify for analysis.
"""
        result = analyze_topics(content)
        result_dict = result.model_dump()
        assert isinstance(result_dict, dict)
        assert "clusters" in result_dict
        assert "lsi_keywords" in result_dict


class TestEdgeCases:
    """Tests for edge cases."""

    def test_empty_content(self):
        """Test handling of empty content."""
        result = analyze_topics("")
        assert result.section_count == 0
        assert result.cluster_count == 0
        assert len(result.recommendations) > 0

    def test_no_h2_headings(self):
        """Test handling when no H2 sections are found."""
        content = """
# Just a Title

Some content without any H2 headings at all.
This should result in zero sections for analysis.
"""
        result = analyze_topics(content)
        assert result.section_count == 0
        assert result.cluster_count == 0

    def test_sections_too_short(self):
        """Test sections with fewer than 20 words are excluded."""
        content = """
# Title

## Short Section

Only a few words here.

## Long Section

This section has more than twenty words to meet the minimum requirement
for being included in the topic clustering analysis process today.
Additional content to ensure we exceed the threshold significantly.
"""
        result = analyze_topics(content)
        # Only the long section should be counted
        assert result.section_count <= 1

    def test_markdown_formatting_handled(self):
        """Test markdown formatting is properly stripped."""
        content = """
# Title

## Section One

This **bold** text and *italic* text should be cleaned properly.
Links like [this link](https://example.com) should also be handled.
`Inline code` and code formatting should be removed from analysis.

## Section Two

More content with **formatting** and [links](http://test.com) here.
Additional text to meet the minimum word count requirements now.
This section should be processed with formatting stripped out.
"""
        result = analyze_topics(content)
        # Should process without errors
        assert isinstance(result, TopicClusteringResult)


class TestRecommendations:
    """Tests for recommendation generation."""

    def test_recommends_more_sections(self):
        """Test recommendation when too few sections."""
        content = """
# Title

## Only Section

This is the only section in the entire document here today.
Not enough sections for meaningful topic clustering analysis.
"""
        result = analyze_topics(content)
        # Should recommend adding more sections
        has_section_rec = any(
            "section" in r.lower() for r in result.recommendations
        )
        assert has_section_rec or result.section_count < 2
