"""Test per validazione llms.txt v2 (#39).

Verifica conformità spec completa: blockquote, warning, companion files, scoring.
"""

from __future__ import annotations

from unittest.mock import MagicMock, patch

from geo_optimizer.core.audit import _validate_llms_content, audit_llms_txt
from geo_optimizer.core.scoring import _score_llms
from geo_optimizer.models.results import LlmsTxtResult

# ─── Contenuti di test ────────────────────────────────────────────────────────

# llms.txt conforme a spec completa
LLMS_FULL_SPEC = """\
# My Project

> A comprehensive tool for optimizing websites for AI search engines.
> Built with Python and FastAPI.

## Features

- [Feature Overview](https://example.com/features)
- [API Reference](https://example.com/api)

## Documentation

- [Getting Started](https://example.com/docs/start)
- [Configuration Guide](https://example.com/docs/config)
- [Advanced Usage](https://example.com/docs/advanced)

## Optional

- [Changelog](https://example.com/changelog)
- [Contributing](https://example.com/contributing)

This is a detailed description of the project that contains enough words
to pass the minimum word count threshold. We need to make sure the file
has at least one hundred words to avoid triggering the too-short warning.
The project supports multiple features including auditing, scoring, and
generating recommendations for improving AI search engine visibility.
It follows the llms.txt specification closely, with proper H1 headings,
blockquote descriptions, organized sections, and markdown links.
Additional content here to ensure we reach the word count requirement
for a complete and valid llms.txt file that passes all checks.
"""

# llms.txt senza blockquote
LLMS_NO_BLOCKQUOTE = """\
# My Project

## Features

- [Feature Overview](https://example.com/features)
- [API Reference](https://example.com/api)

## Documentation

- [Getting Started](https://example.com/docs/start)
- [Configuration Guide](https://example.com/docs/config)
- [Advanced Usage](https://example.com/docs/advanced)

This is a description of the project. We need enough words to pass the
minimum word count threshold. The project supports multiple features
including auditing, scoring, and generating recommendations for sites.
Additional content to reach the hundred word minimum required by the
validation logic and avoid the too-short warning message in results.
"""

# llms.txt troppo corto (< 100 parole)
LLMS_TOO_SHORT = """\
# My Project

> A short description.

## Links

- [Home](https://example.com)
"""

# llms.txt con companion file links (.md)
LLMS_COMPANION = """\
# My Project

> A tool for optimizing AI visibility.

## Documentation

- [API Reference](https://example.com/api.html.md)
- [Guide](https://example.com/guide.md)
- [Feature Overview](https://example.com/features)

## Features

- [Dashboard](https://example.com/dashboard)

This is enough content to pass the word count minimum. We add more text
to reach one hundred words in total. The project supports optimization
for AI search engines with companion markdown files for detailed content.
Additional words to fill the requirement for the validation threshold
so we can properly test companion file detection without short warnings.
"""

# llms.txt con H1 non come prima riga
LLMS_H1_NOT_FIRST = """\
Some random intro text.

# My Project

> A description.

## Features

- [Feature](https://example.com/feature)

This text helps reach the word count minimum. We need to have at least
one hundred words in the file to avoid the too-short warning trigger.
Additional content is added here for the purpose of testing that the
H1-not-first-line warning is correctly generated by the validator.
More filler text to satisfy the word count requirement in our tests.
"""


# ─── Test _validate_llms_content ─────────────────────────────────────────────


class TestValidateLlmsContent:
    """Test per la funzione di validazione interna."""

    def test_full_spec_compliant_no_warnings(self):
        """llms.txt conforme a spec → nessun warning."""
        result = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_description=True,
            has_sections=True,
            has_links=True,
            word_count=len(LLMS_FULL_SPEC.split()),
            links_count=7,
        )
        _validate_llms_content(result, LLMS_FULL_SPEC)

        assert result.has_blockquote is True
        assert result.has_optional_section is True
        assert result.companion_files_hint is False
        assert result.validation_warnings == []

    def test_no_blockquote_generates_warning(self):
        """llms.txt senza blockquote → warning generato."""
        result = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_links=True,
            word_count=len(LLMS_NO_BLOCKQUOTE.split()),
        )
        _validate_llms_content(result, LLMS_NO_BLOCKQUOTE)

        assert result.has_blockquote is False
        assert any("blockquote" in w for w in result.validation_warnings)

    def test_too_short_generates_warning(self):
        """llms.txt troppo corto (< 100 parole) → warning."""
        result = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_links=True,
            word_count=len(LLMS_TOO_SHORT.split()),
        )
        _validate_llms_content(result, LLMS_TOO_SHORT)

        assert any("too short" in w for w in result.validation_warnings)

    def test_companion_files_detected(self):
        """llms.txt con link a file .md → companion_files_hint = True."""
        result = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_links=True,
            word_count=len(LLMS_COMPANION.split()),
            links_count=4,
        )
        _validate_llms_content(result, LLMS_COMPANION)

        assert result.companion_files_hint is True
        assert result.has_blockquote is True

    def test_h1_not_first_line_generates_warning(self):
        """H1 non è la prima riga → warning generato."""
        result = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_links=True,
            word_count=len(LLMS_H1_NOT_FIRST.split()),
        )
        _validate_llms_content(result, LLMS_H1_NOT_FIRST)

        assert any("first line" in w for w in result.validation_warnings)

    def test_no_links_generates_warning(self):
        """llms.txt senza link markdown → warning generato."""
        content = "# My Project\n\n> Description.\n\nSome text without links."
        result = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_links=False,
            word_count=len(content.split()),
        )
        _validate_llms_content(result, content)

        assert any("markdown links" in w for w in result.validation_warnings)

    def test_optional_section_detected(self):
        """Sezione ## Optional viene rilevata."""
        result = LlmsTxtResult(found=True, has_h1=True, has_links=True, word_count=150)
        _validate_llms_content(result, LLMS_FULL_SPEC)

        assert result.has_optional_section is True


# ─── Test scoring con blockquote ─────────────────────────────────────────────


class TestLlmsScoringV2:
    """Test per il punteggio llms.txt con bonus blockquote."""

    def test_blockquote_adds_point(self):
        """has_blockquote = True → +1 punto rispetto a False."""
        llms_base = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_description=True,
            has_sections=True,
            has_links=True,
            word_count=150,
            has_blockquote=False,
        )
        score_no_bq = _score_llms(llms_base)

        llms_with_bq = LlmsTxtResult(
            found=True,
            has_h1=True,
            has_description=True,
            has_sections=True,
            has_links=True,
            word_count=150,
            has_blockquote=True,
        )
        score_with_bq = _score_llms(llms_with_bq)

        assert score_with_bq == score_no_bq + 1

    def test_full_llms_score_unchanged_total(self):
        """Il totale massimo llms rimane 18 con blockquote incluso."""
        from geo_optimizer.models.config import SCORING

        total = (
            SCORING["llms_found"]
            + SCORING["llms_h1"]
            + SCORING["llms_blockquote"]
            + SCORING["llms_sections"]
            + SCORING["llms_links"]
            + SCORING["llms_depth"]
            + SCORING["llms_depth_high"]
            + SCORING["llms_full"]
        )
        assert total == 18


# ─── Test integrazione audit_llms_txt con mock HTTP ──────────────────────────


class TestAuditLlmsTxtV2Integration:
    """Test integrazione: audit_llms_txt popola i nuovi campi v2."""

    @patch("geo_optimizer.core.audit.fetch_url")
    def test_full_spec_via_audit(self, mock_fetch):
        """audit_llms_txt con contenuto conforme popola tutti i campi v2."""
        # Mock: prima chiamata = llms.txt, seconda = llms-full.txt
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.text = LLMS_FULL_SPEC

        mock_full_404 = MagicMock()
        mock_full_404.status_code = 404

        mock_fetch.side_effect = [
            (mock_response, None),  # /llms.txt
            (mock_full_404, None),  # /llms-full.txt
        ]

        result = audit_llms_txt("https://example.com")

        assert result.found is True
        assert result.has_h1 is True
        assert result.has_blockquote is True
        assert result.has_optional_section is True
        assert result.companion_files_hint is False
        assert result.validation_warnings == []

    @patch("geo_optimizer.core.audit.fetch_url")
    def test_no_blockquote_via_audit(self, mock_fetch):
        """audit_llms_txt senza blockquote genera warning."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.text = LLMS_NO_BLOCKQUOTE

        mock_full_404 = MagicMock()
        mock_full_404.status_code = 404

        mock_fetch.side_effect = [
            (mock_response, None),
            (mock_full_404, None),
        ]

        result = audit_llms_txt("https://example.com")

        assert result.has_blockquote is False
        assert any("blockquote" in w for w in result.validation_warnings)
