"""Tests for cortex.hippo.large_files — large-file interception pipeline."""

from __future__ import annotations

import asyncio
import pytest

from cortex.hippo.large_files import (
    ExplorationSummaryInput,
    FileBlock,
    explore_code,
    explore_structured_data,
    explore_text,
    extension_from_name_or_mime,
    extract_file_ids_from_content,
    format_file_reference,
    generate_exploration_summary,
    is_code,
    is_structured,
    parse_file_blocks,
    _explore_text_deterministic_fallback,
)


# ---------------------------------------------------------------------------
# parse_file_blocks
# ---------------------------------------------------------------------------


class TestParseFileBlocks:
    def test_basic_block(self):
        content = '<file name="test.py" mime="text/x-python">print("hi")</file>'
        blocks = parse_file_blocks(content)
        assert len(blocks) == 1
        b = blocks[0]
        assert b.file_name == "test.py"
        assert b.mime_type == "text/x-python"
        assert b.text == 'print("hi")'
        assert b.start == 0
        assert b.end == len(content)

    def test_multiple_blocks(self):
        content = (
            'Before <file name="a.json">{"a":1}</file> '
            'middle <file name="b.csv">h1,h2\n1,2</file> after'
        )
        blocks = parse_file_blocks(content)
        assert len(blocks) == 2
        assert blocks[0].file_name == "a.json"
        assert blocks[1].file_name == "b.csv"

    def test_attributes_with_single_quotes(self):
        content = "<file name='data.xml' mime='text/xml'>root</file>"
        blocks = parse_file_blocks(content)
        assert len(blocks) == 1
        assert blocks[0].file_name == "data.xml"
        assert blocks[0].mime_type == "text/xml"

    def test_unquoted_attributes(self):
        content = "<file name=readme.md>hello</file>"
        blocks = parse_file_blocks(content)
        assert len(blocks) == 1
        assert blocks[0].file_name == "readme.md"

    def test_empty_content(self):
        blocks = parse_file_blocks("")
        assert blocks == []

    def test_no_file_blocks(self):
        blocks = parse_file_blocks("Just some text without any file blocks.")
        assert blocks == []

    def test_empty_file_block(self):
        content = "<file name='empty.txt'></file>"
        blocks = parse_file_blocks(content)
        assert len(blocks) == 1
        assert blocks[0].text == ""

    def test_case_insensitive(self):
        content = '<FILE name="upper.py">code</FILE>'
        blocks = parse_file_blocks(content)
        assert len(blocks) == 1
        assert blocks[0].file_name == "upper.py"


# ---------------------------------------------------------------------------
# extract_file_ids_from_content
# ---------------------------------------------------------------------------


class TestExtractFileIds:
    def test_basic_ids(self):
        content = "See file_abcdef0123456789 and file_1234567890abcdef for details."
        ids = extract_file_ids_from_content(content)
        assert ids == ["file_abcdef0123456789", "file_1234567890abcdef"]

    def test_deduplication(self):
        content = "file_aaaa000000000000 and file_AAAA000000000000 again"
        ids = extract_file_ids_from_content(content)
        assert len(ids) == 1
        assert ids[0] == "file_aaaa000000000000"

    def test_no_ids(self):
        assert extract_file_ids_from_content("no files here") == []

    def test_partial_ids_ignored(self):
        # Too short — only 12 hex chars
        assert extract_file_ids_from_content("file_abcdef012345") == []


# ---------------------------------------------------------------------------
# format_file_reference
# ---------------------------------------------------------------------------


class TestFormatFileReference:
    def test_basic_format(self):
        result = format_file_reference(
            file_id="file_abc123",
            byte_size=1024,
            summary="A test file.",
            file_name="test.py",
            mime_type="text/x-python",
        )
        assert "file_abc123" in result
        assert "test.py" in result
        assert "text/x-python" in result
        assert "1,024 bytes" in result
        assert "A test file." in result

    def test_missing_name_and_mime(self):
        result = format_file_reference(
            file_id="file_000",
            byte_size=0,
            summary="",
        )
        assert "unknown" in result
        assert "(no summary available)" in result

    def test_negative_byte_size(self):
        result = format_file_reference(
            file_id="file_x",
            byte_size=-5,
            summary="neg",
        )
        assert "0 bytes" in result


# ---------------------------------------------------------------------------
# extension_from_name_or_mime
# ---------------------------------------------------------------------------


class TestExtensionFromNameOrMime:
    def test_from_filename(self):
        assert extension_from_name_or_mime("script.py") == "py"

    def test_from_mime(self):
        assert extension_from_name_or_mime(mime_type="application/json") == "json"

    def test_filename_takes_priority(self):
        assert extension_from_name_or_mime("data.csv", "application/json") == "csv"

    def test_fallback_to_txt(self):
        assert extension_from_name_or_mime() == "txt"

    def test_path_with_dirs(self):
        assert extension_from_name_or_mime("/some/path/to/file.rs") == "rs"

    def test_dotfile_no_extension(self):
        assert extension_from_name_or_mime(".gitignore", "text/plain") == "txt"


# ---------------------------------------------------------------------------
# explore_structured_data — JSON
# ---------------------------------------------------------------------------


class TestExploreStructuredJson:
    def test_json_object(self):
        result = explore_structured_data('{"name": "test", "value": 42}', file_name="data.json")
        assert "JSON" in result
        assert "object" in result.lower()

    def test_json_array(self):
        result = explore_structured_data('[1, 2, 3]', file_name="arr.json")
        assert "array" in result

    def test_invalid_json(self):
        result = explore_structured_data("{bad json", file_name="broken.json")
        assert "failed to parse" in result.lower()

    def test_json_via_mime(self):
        result = explore_structured_data('{"a":1}', mime_type="application/json")
        assert "JSON" in result


# ---------------------------------------------------------------------------
# explore_structured_data — CSV
# ---------------------------------------------------------------------------


class TestExploreStructuredCsv:
    def test_basic_csv(self):
        content = "name,age,city\nAlice,30,NYC\nBob,25,LA"
        result = explore_structured_data(content, file_name="people.csv")
        assert "CSV" in result
        assert "Rows: 2" in result
        assert "name" in result
        assert "age" in result

    def test_empty_csv(self):
        result = explore_structured_data("", file_name="empty.csv")
        assert "no rows found" in result.lower()

    def test_tsv(self):
        content = "col1\tcol2\nval1\tval2"
        result = explore_structured_data(content, file_name="data.tsv")
        assert "TSV" in result


# ---------------------------------------------------------------------------
# explore_structured_data — YAML / XML
# ---------------------------------------------------------------------------


class TestExploreStructuredYamlXml:
    def test_yaml(self):
        content = "name: test\nversion: 1.0\ndependencies:\n  - foo\n  - bar\nscripts:\n  build: make"
        result = explore_structured_data(content, file_name="config.yaml")
        assert "YAML" in result
        # Only keys with no inline value are detected as top-level
        assert "dependencies" in result
        assert "scripts" in result

    def test_yml_extension(self):
        content = "key: value"
        result = explore_structured_data(content, file_name="config.yml")
        assert "YAML" in result

    def test_xml(self):
        content = "<root><item>1</item><item>2</item></root>"
        result = explore_structured_data(content, file_name="data.xml")
        assert "XML" in result
        assert "root" in result
        assert "item" in result


# ---------------------------------------------------------------------------
# explore_code
# ---------------------------------------------------------------------------


class TestExploreCodePython:
    def test_python_code(self):
        content = "\n".join([
            "import os",
            "from pathlib import Path",
            "",
            "def hello(name: str) -> str:",
            '    return f"Hello {name}"',
            "",
            "class MyClass:",
            "    pass",
        ])
        result = explore_code(content, "main.py")
        assert "main.py" in result
        assert "import os" in result
        assert "def hello" in result
        assert "class MyClass" in result

    def test_no_imports(self):
        result = explore_code("x = 1\ny = 2\n")
        assert "none detected" in result


class TestExploreCodeJavascript:
    def test_js_code(self):
        content = "\n".join([
            'const fs = require("fs")',
            'import { foo } from "bar"',
            "",
            "export function processData(input) {",
            "  return input;",
            "}",
            "",
            "export async function fetchData() {}",
            "",
            "class DataHandler {}",
        ])
        result = explore_code(content, "app.js")
        assert "app.js" in result
        assert "require" in result
        assert "processData" in result or "function processData" in result
        assert "class DataHandler" in result


# ---------------------------------------------------------------------------
# explore_text (deterministic)
# ---------------------------------------------------------------------------


class TestExploreTextDeterministic:
    def test_basic_text(self):
        content = "# Hello World\n\nThis is a test document.\n\n## Section Two\n\nMore content here."
        result = _explore_text_deterministic_fallback(content, "readme.md")
        assert "readme.md" in result
        assert "Characters:" in result
        assert "Words:" in result
        assert "Lines:" in result
        assert "# Hello World" in result

    def test_empty_text(self):
        result = _explore_text_deterministic_fallback("")
        assert "Characters: 0" in result
        assert "(empty)" in result

    @pytest.mark.asyncio
    async def test_explore_text_with_summarizer(self):
        async def mock_summarizer(prompt: str) -> str:
            return "LLM-generated summary of the document."

        inp = ExplorationSummaryInput(
            content="A " * 1000,
            file_name="big.txt",
            summarize_text=mock_summarizer,
        )
        result = await explore_text(inp)
        assert result == "LLM-generated summary of the document."

    @pytest.mark.asyncio
    async def test_explore_text_summarizer_failure_falls_back(self):
        async def failing_summarizer(prompt: str) -> str:
            raise RuntimeError("LLM unavailable")

        inp = ExplorationSummaryInput(
            content="Hello world content",
            file_name="doc.txt",
            summarize_text=failing_summarizer,
        )
        result = await explore_text(inp)
        assert "Text exploration summary" in result
        assert "doc.txt" in result

    @pytest.mark.asyncio
    async def test_explore_text_summarizer_returns_empty(self):
        async def empty_summarizer(prompt: str) -> str:
            return "   "

        inp = ExplorationSummaryInput(
            content="Some content here",
            summarize_text=empty_summarizer,
        )
        result = await explore_text(inp)
        # Should fall back to deterministic
        assert "Text exploration summary" in result


# ---------------------------------------------------------------------------
# generate_exploration_summary (routing)
# ---------------------------------------------------------------------------


class TestGenerateExplorationSummaryRouting:
    @pytest.mark.asyncio
    async def test_routes_to_structured(self):
        inp = ExplorationSummaryInput(
            content='{"key": "value"}',
            file_name="data.json",
        )
        result = await generate_exploration_summary(inp)
        assert "JSON" in result

    @pytest.mark.asyncio
    async def test_routes_to_code(self):
        inp = ExplorationSummaryInput(
            content="def foo():\n    pass\n",
            file_name="script.py",
        )
        result = await generate_exploration_summary(inp)
        assert "Code exploration summary" in result

    @pytest.mark.asyncio
    async def test_routes_to_text(self):
        inp = ExplorationSummaryInput(
            content="Just some plain text content.",
            file_name="notes.txt",
        )
        result = await generate_exploration_summary(inp)
        assert "Text exploration summary" in result

    @pytest.mark.asyncio
    async def test_routes_by_mime(self):
        inp = ExplorationSummaryInput(
            content="col1,col2\nval1,val2",
            mime_type="text/csv",
        )
        result = await generate_exploration_summary(inp)
        assert "CSV" in result


# ---------------------------------------------------------------------------
# is_structured / is_code
# ---------------------------------------------------------------------------


class TestIsStructured:
    def test_by_extension(self):
        assert is_structured(extension="json") is True
        assert is_structured(extension="csv") is True
        assert is_structured(extension="yaml") is True
        assert is_structured(extension="yml") is True
        assert is_structured(extension="xml") is True
        assert is_structured(extension="tsv") is True

    def test_by_mime(self):
        assert is_structured(mime_type="application/json") is True
        assert is_structured(mime_type="text/csv") is True

    def test_not_structured(self):
        assert is_structured(extension="py") is False
        assert is_structured(extension="txt") is False
        assert is_structured(mime_type="text/plain") is False


class TestIsCode:
    def test_by_extension(self):
        assert is_code(extension="py") is True
        assert is_code(extension="js") is True
        assert is_code(extension="rs") is True
        assert is_code(extension="go") is True
        assert is_code(extension="ts") is True

    def test_by_mime(self):
        assert is_code(mime_type="text/x-python") is True
        assert is_code(mime_type="application/javascript") is True

    def test_not_code(self):
        assert is_code(extension="json") is False
        assert is_code(extension="txt") is False
        assert is_code(mime_type="text/plain") is False
