"""Tests for bex.grammar_index — runtime grammar lookup."""

import os
import tempfile

import pytest
import yaml

from bex.grammar_index import GrammarIndex, load_grammar_index


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------

def _make_yml(tmp_path, entries):
    """Write a grammars.yml with the given entries grouped by module."""
    dervish = tmp_path / ".dervish"
    dervish.mkdir()
    data = {"test_module": entries}
    with open(dervish / "grammars.yml", "w") as f:
        yaml.dump(data, f, default_flow_style=False)


SAMPLE_ENTRIES = [
    {
        "package": "src/services [return]",
        "methods": 10,
        "grammar": "return.ok+?.error?",
        "score": 0.8,
        "algorithm": "CRX",
        "mdl": 12.0,
    },
    {
        "package": "src/services [if]",
        "methods": 5,
        "grammar": "if.cond+.then+.else?",
        "score": 0.6,
        "algorithm": "CRX",
        "mdl": 15.0,
    },
    {
        "package": "src/utils [return]",
        "methods": 8,
        "grammar": "return.value+",
        "score": 1.0,
        "algorithm": "CRX",
        "mdl": 5.0,
    },
]


# ---------------------------------------------------------------------------
# GrammarIndex construction
# ---------------------------------------------------------------------------

class TestGrammarIndex:

    def test_parse_label_with_symbol(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        pkgs = idx.packages()
        assert "src/services" in pkgs
        assert "src/utils" in pkgs

    def test_parse_label_without_symbol(self, tmp_path):
        entries = [{"package": "src/root", "grammar": "a.b", "score": 0.5}]
        idx = GrammarIndex(str(tmp_path), entries)
        assert "src/root" in idx.packages()

    def test_sorted_by_score_desc(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        entries = idx.get_package("src/services")
        scores = [s for _, _, s, _ in entries]
        assert scores == sorted(scores, reverse=True)

    def test_repr(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        assert "3 entries" in repr(idx)
        assert "2 packages" in repr(idx)


# ---------------------------------------------------------------------------
# get()
# ---------------------------------------------------------------------------

class TestGet:

    def test_get_best_no_context(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        g = idx.get("src/services/main.kt")
        assert g == "return.ok+?.error?"

    def test_get_by_context_symbol(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        g = idx.get("src/services/main.kt", context_symbol="if")
        assert g == "if.cond+.then+.else?"

    def test_get_missing_context_falls_back_to_best(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        g = idx.get("src/services/main.kt", context_symbol="nonexistent")
        assert g == "return.ok+?.error?"

    def test_get_unknown_package_returns_none(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        g = idx.get("src/unknown/file.kt")
        assert g is None

    def test_get_absolute_path(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        abs_path = str(tmp_path / "src" / "services" / "main.kt")
        g = idx.get(abs_path, context_symbol="return")
        assert g == "return.ok+?.error?"


# ---------------------------------------------------------------------------
# get_package()
# ---------------------------------------------------------------------------

class TestGetPackage:

    def test_returns_all_entries(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        entries = idx.get_package("src/services/main.kt")
        assert len(entries) == 2

    def test_unknown_package_returns_empty(self, tmp_path):
        idx = GrammarIndex(str(tmp_path), SAMPLE_ENTRIES)
        entries = idx.get_package("src/unknown/file.kt")
        assert entries == []


# ---------------------------------------------------------------------------
# load_grammar_index()
# ---------------------------------------------------------------------------

class TestLoadGrammarIndex:

    def test_load_existing(self, tmp_path):
        _make_yml(tmp_path, SAMPLE_ENTRIES)
        idx = load_grammar_index(str(tmp_path))
        assert len(idx.get_all()) == 3
        assert "src/services" in idx.packages()

    def test_load_missing_file(self, tmp_path):
        idx = load_grammar_index(str(tmp_path))
        assert len(idx.get_all()) == 0
        assert idx.packages() == []

    def test_roundtrip_persisted_ragsak(self):
        """Integration test: grammars.yml generated by analyze_directory."""
        ragsak_yml = "/home/tobi/Desktop/kesai/RAGSAK/.dervish/grammars.yml"
        if not os.path.exists(ragsak_yml):
            pytest.skip("RAGSAK grammars.yml not generated yet")
        idx = load_grammar_index("/home/tobi/Desktop/kesai/RAGSAK")
        assert len(idx.get_all()) > 0
        # Should be able to resolve a file in the storage package
        g = idx.get(
            "/home/tobi/Desktop/kesai/RAGSAK/infrastructure/adapters/search/src/main/kotlin/eu/corentic/springrag/service/storage/GcsStorage.kt",
            context_symbol="return",
        )
        assert g is not None
        assert "return" in g
