"""Tests for text chunking strategies."""

import pytest

from bifrost_rag.chunking import Chunk, FixedSizeChunker, RecursiveChunker, SentenceChunker


class TestChunk:
    def test_chunk_creation(self):
        chunk = Chunk(text="hello world", index=0, metadata={"source": "test"})
        assert chunk.text == "hello world"
        assert chunk.index == 0
        assert chunk.metadata["source"] == "test"

    def test_chunk_length(self):
        chunk = Chunk(text="hello world", index=0)
        assert len(chunk) == 11

    def test_chunk_str(self):
        chunk = Chunk(text="hello", index=0)
        assert str(chunk) == "hello"


class TestFixedSizeChunker:
    def test_basic_chunking(self):
        chunker = FixedSizeChunker(chunk_size=10, overlap=0)
        chunks = chunker.chunk("0123456789abcdefghij")
        assert len(chunks) == 2
        assert chunks[0].text == "0123456789"
        assert chunks[1].text == "abcdefghij"

    def test_chunking_with_overlap(self):
        chunker = FixedSizeChunker(chunk_size=10, overlap=3)
        chunks = chunker.chunk("0123456789abcdefghij")
        assert len(chunks) >= 2
        # First chunk is 0-9, second starts at 7 (10-3=7)
        assert chunks[0].text == "0123456789"
        assert chunks[1].text.startswith("789")

    def test_small_text_single_chunk(self):
        chunker = FixedSizeChunker(chunk_size=100, overlap=0)
        chunks = chunker.chunk("short text")
        assert len(chunks) == 1
        assert chunks[0].text == "short text"

    def test_empty_text(self):
        chunker = FixedSizeChunker(chunk_size=10, overlap=0)
        chunks = chunker.chunk("")
        assert len(chunks) == 0

    def test_chunk_indices_sequential(self):
        chunker = FixedSizeChunker(chunk_size=5, overlap=0)
        chunks = chunker.chunk("hello world test")
        for i, chunk in enumerate(chunks):
            assert chunk.index == i

    def test_metadata_propagated(self):
        chunker = FixedSizeChunker(chunk_size=10, overlap=0)
        chunks = chunker.chunk("0123456789ab", metadata={"source": "doc1"})
        for chunk in chunks:
            assert chunk.metadata["source"] == "doc1"

    def test_overlap_must_be_less_than_size(self):
        with pytest.raises(ValueError):
            FixedSizeChunker(chunk_size=10, overlap=10)


class TestSentenceChunker:
    def test_basic_sentence_split(self):
        chunker = SentenceChunker(max_sentences=2)
        text = "First sentence. Second sentence. Third sentence."
        chunks = chunker.chunk(text)
        assert len(chunks) == 2
        assert "First sentence" in chunks[0].text
        assert "Second sentence" in chunks[0].text

    def test_single_sentence(self):
        chunker = SentenceChunker(max_sentences=5)
        text = "Just one sentence."
        chunks = chunker.chunk(text)
        assert len(chunks) == 1

    def test_empty_text(self):
        chunker = SentenceChunker(max_sentences=2)
        chunks = chunker.chunk("")
        assert len(chunks) == 0

    def test_preserves_sentence_boundaries(self):
        chunker = SentenceChunker(max_sentences=1)
        text = "Hello world. Goodbye world."
        chunks = chunker.chunk(text)
        assert len(chunks) == 2
        assert chunks[0].text.strip() == "Hello world."
        assert chunks[1].text.strip() == "Goodbye world."


class TestRecursiveChunker:
    def test_splits_by_paragraphs_first(self):
        chunker = RecursiveChunker(chunk_size=30, overlap=0)
        text = "First paragraph.\n\nSecond paragraph."
        chunks = chunker.chunk(text)
        assert len(chunks) == 2
        assert "First" in chunks[0].text
        assert "Second" in chunks[1].text

    def test_falls_back_to_sentences(self):
        chunker = RecursiveChunker(chunk_size=30, overlap=0)
        text = "Short sentence. Another short sentence."
        chunks = chunker.chunk(text)
        assert len(chunks) >= 1
        for chunk in chunks:
            assert len(chunk.text) <= 30 or len(chunk.text.split(". ")) == 1

    def test_respects_max_chunk_size(self):
        chunker = RecursiveChunker(chunk_size=20, overlap=0)
        text = "A " * 50
        chunks = chunker.chunk(text)
        # All chunks except possibly the last should be <= chunk_size
        for chunk in chunks[:-1]:
            assert len(chunk.text) <= 25  # some tolerance for boundary

    def test_empty_text(self):
        chunker = RecursiveChunker(chunk_size=100, overlap=0)
        chunks = chunker.chunk("")
        assert len(chunks) == 0
