"""Unit tests for chunk quality scoring."""

from __future__ import annotations

import pytest

from extractor.chunker import _compute_quality
from extractor.schema import ChunkQuality

# ---------------------------------------------------------------------------
# _compute_quality
# ---------------------------------------------------------------------------


def test_compute_quality_basic():
    text = "The quick brown fox jumps over the lazy dog. " * 5
    q = _compute_quality(text, section_path=["Introduction"], entity_count=2, token_count=50)
    assert 0.0 <= q.lexical_density <= 1.0
    assert q.entity_density >= 0.0
    assert 0.0 <= q.compression_ratio <= 1.0
    assert q.heading_coverage == pytest.approx(0.25)  # 1 of 4 tiers
    assert q.text_length == len(text)


def test_compute_quality_returns_chunk_quality_instance():
    text = "Some sample text."
    q = _compute_quality(text, section_path=[], entity_count=0, token_count=5)
    assert isinstance(q, ChunkQuality)


def test_compute_quality_empty_text():
    q = _compute_quality("", section_path=[], entity_count=0, token_count=1)
    assert q.lexical_density == 0.0
    assert q.text_length == 0
    assert q.heading_coverage == 0.0
    # compression_ratio for empty bytes: compressed may be larger than raw
    assert isinstance(q.compression_ratio, float)


def test_compute_quality_full_section_path():
    text = "Some content here for testing purposes."
    q = _compute_quality(text, section_path=["A", "B", "C", "D"], entity_count=0, token_count=10)
    assert q.heading_coverage == pytest.approx(1.0)  # 4/4


def test_compute_quality_section_path_overflow():
    """Section paths longer than 4 are capped at 1.0."""
    text = "Content text here."
    q = _compute_quality(
        text, section_path=["A", "B", "C", "D", "E"], entity_count=0, token_count=5
    )
    assert q.heading_coverage == pytest.approx(1.0)  # min(5/4, 1.0)


def test_compute_quality_high_entity_density():
    text = "Apple Google Microsoft Amazon."
    q = _compute_quality(text, section_path=[], entity_count=4, token_count=5)
    assert q.entity_density == pytest.approx(80.0)  # 4/5 * 100


def test_compute_quality_zero_entity_density():
    text = "No entities here at all."
    q = _compute_quality(text, section_path=[], entity_count=0, token_count=10)
    assert q.entity_density == pytest.approx(0.0)


def test_compute_quality_lexical_density_range():
    """Lexical density is always in [0, 1]."""
    # Highly repetitive text → low density
    repetitive = "the the the the the the the the the the"
    q = _compute_quality(repetitive, section_path=[], entity_count=0, token_count=10)
    assert 0.0 <= q.lexical_density <= 1.0

    # All unique words → high density
    diverse = "apple banana cherry date elderberry fig grape hazel"
    q2 = _compute_quality(diverse, section_path=[], entity_count=0, token_count=8)
    assert q2.lexical_density > q.lexical_density


def test_compute_quality_text_length():
    text = "Hello world"
    q = _compute_quality(text, section_path=[], entity_count=0, token_count=2)
    assert q.text_length == len("Hello world")


def test_compute_quality_values_are_rounded():
    """Returned float values should be rounded to 4 decimal places."""
    text = "The quick brown fox jumps over the lazy dog."
    q = _compute_quality(text, section_path=["Intro"], entity_count=1, token_count=10)
    # Check that rounding was applied (no more than 4 decimal places)
    for val in [q.lexical_density, q.entity_density, q.compression_ratio, q.heading_coverage]:
        # round-trip through 4dp shouldn't change the value
        assert round(val, 4) == val


# ---------------------------------------------------------------------------
# ChunkQuality schema
# ---------------------------------------------------------------------------


def test_chunk_quality_schema_fields():
    q = ChunkQuality(
        lexical_density=0.8,
        entity_density=2.5,
        compression_ratio=0.4,
        heading_coverage=0.5,
        text_length=200,
    )
    d = q.model_dump()
    assert d["lexical_density"] == 0.8
    assert d["entity_density"] == 2.5
    assert d["compression_ratio"] == 0.4
    assert d["heading_coverage"] == 0.5
    assert d["text_length"] == 200


def test_chunk_quality_extra_fields_forbidden():
    """ChunkQuality uses extra='forbid'."""
    from pydantic import ValidationError

    with pytest.raises(ValidationError):
        ChunkQuality(
            lexical_density=0.8,
            entity_density=2.5,
            compression_ratio=0.4,
            heading_coverage=0.5,
            text_length=200,
            unknown_field="oops",
        )


def test_chunk_quality_zero_values():
    q = ChunkQuality(
        lexical_density=0.0,
        entity_density=0.0,
        compression_ratio=0.0,
        heading_coverage=0.0,
        text_length=0,
    )
    assert q.text_length == 0
    assert q.lexical_density == 0.0
