"""Tests for RAG document types and loaders."""
import pytest

from logos_adk.rag import Document, Chunk, DocumentLoadError


class TestDocument:
    def test_create_document(self):
        doc = Document(content="hello world", metadata={"source": "test.txt"})
        assert doc.content == "hello world"
        assert doc.metadata["source"] == "test.txt"
        assert doc.id

    def test_document_ids_unique(self):
        d1 = Document(content="a", metadata={})
        d2 = Document(content="b", metadata={})
        assert d1.id != d2.id


class TestChunk:
    def test_create_chunk(self):
        chunk = Chunk(
            content="piece of text",
            metadata={"source": "test.txt", "chunk_index": 0},
            document_id="doc-1",
        )
        assert chunk.content == "piece of text"
        assert chunk.document_id == "doc-1"
        assert chunk.embedding is None
        assert chunk.id

    def test_chunk_with_embedding(self):
        chunk = Chunk(
            content="text",
            metadata={},
            document_id="doc-1",
            embedding=[0.1, 0.2, 0.3],
        )
        assert chunk.embedding == [0.1, 0.2, 0.3]


class TestDocumentLoadError:
    def test_is_exception(self):
        err = DocumentLoadError("file not found")
        assert isinstance(err, Exception)
        assert str(err) == "file not found"


from logos_adk.rag.loaders import TextLoader, MarkdownLoader


class TestTextLoader:
    def test_supports_txt(self):
        loader = TextLoader()
        assert loader.supports("file.txt") is True
        assert loader.supports("file.pdf") is False

    @pytest.mark.asyncio
    async def test_load_txt_file(self, tmp_path):
        f = tmp_path / "test.txt"
        f.write_text("Hello world")
        docs = await TextLoader().load(str(f))
        assert len(docs) == 1
        assert docs[0].content == "Hello world"
        assert docs[0].metadata["source"] == str(f)

    @pytest.mark.asyncio
    async def test_load_missing_file(self):
        with pytest.raises(DocumentLoadError, match="not found"):
            await TextLoader().load("/nonexistent/file.txt")

    @pytest.mark.asyncio
    async def test_load_raw_string(self):
        loader = TextLoader()
        docs = await loader.load_text("raw content", metadata={"tag": "test"})
        assert len(docs) == 1
        assert docs[0].content == "raw content"
        assert docs[0].metadata["tag"] == "test"


class TestMarkdownLoader:
    def test_supports_md(self):
        loader = MarkdownLoader()
        assert loader.supports("README.md") is True
        assert loader.supports("file.txt") is False

    @pytest.mark.asyncio
    async def test_load_md_file(self, tmp_path):
        f = tmp_path / "doc.md"
        f.write_text("# Title\n\nContent here.\n\n## Section\n\nMore content.")
        docs = await MarkdownLoader().load(str(f))
        assert len(docs) == 1
        assert "# Title" in docs[0].content
        assert docs[0].metadata["source"] == str(f)

    @pytest.mark.asyncio
    async def test_extracts_title(self, tmp_path):
        f = tmp_path / "doc.md"
        f.write_text("# My Title\n\nBody text.")
        docs = await MarkdownLoader().load(str(f))
        assert docs[0].metadata.get("title") == "My Title"


from logos_adk.rag.loaders import CSVLoader, JSONLoader, HTMLLoader, URLLoader


class TestCSVLoader:
    def test_supports_csv(self):
        loader = CSVLoader()
        assert loader.supports("data.csv") is True
        assert loader.supports("data.txt") is False

    @pytest.mark.asyncio
    async def test_load_csv(self, tmp_path):
        f = tmp_path / "test.csv"
        f.write_text("name,age\nAlice,30\nBob,25\n")
        loader = CSVLoader()
        docs = await loader.load(str(f))
        assert len(docs) == 2
        assert "Alice" in docs[0].content
        assert docs[0].metadata["row_index"] == 0

    @pytest.mark.asyncio
    async def test_load_csv_missing(self):
        loader = CSVLoader()
        with pytest.raises(DocumentLoadError):
            await loader.load("/fake/missing.csv")


class TestJSONLoader:
    def test_supports_json(self):
        loader = JSONLoader()
        assert loader.supports("data.json") is True
        assert loader.supports("data.jsonl") is True
        assert loader.supports("data.txt") is False

    @pytest.mark.asyncio
    async def test_load_json_array(self, tmp_path):
        import json
        f = tmp_path / "data.json"
        f.write_text(json.dumps([{"text": "hello"}, {"text": "world"}]))
        loader = JSONLoader(content_key="text")
        docs = await loader.load(str(f))
        assert len(docs) == 2
        assert docs[0].content == "hello"

    @pytest.mark.asyncio
    async def test_load_jsonl(self, tmp_path):
        f = tmp_path / "data.jsonl"
        f.write_text('{"text":"line1"}\n{"text":"line2"}\n')
        loader = JSONLoader(content_key="text")
        docs = await loader.load(str(f))
        assert len(docs) == 2

    @pytest.mark.asyncio
    async def test_load_json_no_content_key(self, tmp_path):
        import json
        f = tmp_path / "data.json"
        f.write_text(json.dumps([{"a": 1}, {"b": 2}]))
        loader = JSONLoader()
        docs = await loader.load(str(f))
        assert len(docs) == 2
        assert "1" in docs[0].content


class TestHTMLLoader:
    def test_supports_html(self):
        loader = HTMLLoader()
        assert loader.supports("page.html") is True
        assert loader.supports("page.htm") is True
        assert loader.supports("page.txt") is False

    @pytest.mark.asyncio
    async def test_load_html(self, tmp_path):
        f = tmp_path / "page.html"
        f.write_text("<html><head><title>Test</title></head><body><p>Hello world</p></body></html>")
        loader = HTMLLoader()
        docs = await loader.load(str(f))
        assert len(docs) == 1
        assert "Hello world" in docs[0].content
        assert docs[0].metadata.get("title") == "Test"

    @pytest.mark.asyncio
    async def test_strips_tags(self, tmp_path):
        f = tmp_path / "page.html"
        f.write_text("<b>bold</b> and <i>italic</i>")
        loader = HTMLLoader()
        docs = await loader.load(str(f))
        assert "<b>" not in docs[0].content
        assert "bold" in docs[0].content


class TestURLLoader:
    def test_supports_url(self):
        loader = URLLoader()
        assert loader.supports("https://example.com") is True
        assert loader.supports("http://example.com/page") is True
        assert loader.supports("file.txt") is False

    @pytest.mark.asyncio
    async def test_load_url(self, monkeypatch):
        import httpx

        class FakeResponse:
            status_code = 200
            text = "<html><head><title>Test</title></head><body>Content</body></html>"
            headers = {"content-type": "text/html"}
            def raise_for_status(self): pass

        class FakeClient:
            async def __aenter__(self):
                return self
            async def __aexit__(self, *args):
                pass
            async def get(self, url):
                return FakeResponse()

        monkeypatch.setattr(httpx, "AsyncClient", lambda: FakeClient())
        loader = URLLoader()
        docs = await loader.load("https://example.com")
        assert len(docs) >= 1
        assert "Content" in docs[0].content
        assert docs[0].metadata.get("url") == "https://example.com"
