# #region Test.ContextAwarePromptBuilder [C:2] [TYPE Module] [SEMANTICS test,prompt,builder,context]
# @BRIEF Tests for prompt_builder.py — ContextAwarePromptBuilder.
# @RELATION BINDS_TO -> [ContextAwarePromptBuilder]

import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))

import pytest
from unittest.mock import MagicMock

from src.plugins.translate.prompt_builder import ContextAwarePromptBuilder


class TestRenderEntry:
    """ContextAwarePromptBuilder.render_entry."""

    def test_basic_entry_dict(self):
        """Render a dict entry."""
        entry = {"source_term": "hello", "target_term": "bonjour"}
        line = ContextAwarePromptBuilder.render_entry(entry)
        assert line == '"hello" -> "bonjour"'

    def test_basic_entry_object(self):
        """Render an object entry."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = False
        entry.context_data = None
        entry.usage_notes = None

        line = ContextAwarePromptBuilder.render_entry(entry)
        assert line == '"hello" -> "bonjour"'

    def test_with_context(self):
        """Entry with context data."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = True
        entry.context_data = {"table": "users", "column": "name"}
        entry.usage_notes = None

        line = ContextAwarePromptBuilder.render_entry(entry)
        assert "table=users" in line
        assert "column=name" in line
        assert "context:" in line

    def test_with_context_dict_str(self):
        """Entry with context_data as JSON string."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = True
        entry.context_data = '{"table": "users"}'
        entry.usage_notes = None

        line = ContextAwarePromptBuilder.render_entry(entry)
        assert "table=users" in line

    def test_with_context_invalid_json(self):
        """Entry with context_data as non-JSON string."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = True
        entry.context_data = "plain string context"
        entry.usage_notes = None

        line = ContextAwarePromptBuilder.render_entry(entry)
        assert "plain string context" in line

    def test_with_context_string_direct(self):
        """Entry with context_data as string (not dict, not valid JSON)."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = True
        entry.context_data = "some raw context"
        entry.usage_notes = None

        line = ContextAwarePromptBuilder.render_entry(entry, priority=True)
        assert "PRIORITY" in line

    def test_usage_notes(self):
        """Entry with usage notes."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = False
        entry.context_data = None
        entry.usage_notes = "Formal greeting"

        line = ContextAwarePromptBuilder.render_entry(entry)
        assert "Usage: Formal greeting" in line

    def test_priority(self):
        """Priority entry gets prefix."""
        entry = {"source_term": "hello", "target_term": "bonjour"}
        line = ContextAwarePromptBuilder.render_entry(entry, priority=True)
        assert line.startswith("# PRIORITY")

    def test_context_truncation(self):
        """Very long context is truncated."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = True
        entry.context_data = {"long": "x" * 3000}
        entry.usage_notes = None

        line = ContextAwarePromptBuilder.render_entry(entry)
        assert len(line) < 4000
        assert "...[truncated]" in line

    def test_context_items_dict_access(self):
        """Context items from dict-type entry."""
        entry = {
            "source_term": "hello", "target_term": "bonjour",
            "has_context": True, "context_data": {"key": "value"},
            "usage_notes": None,
        }
        line = ContextAwarePromptBuilder.render_entry(entry)
        assert "key=value" in line

    def test_context_data_json_array(self):
        """Context_data as JSON array string — parsed as non-dict (line 69)."""
        entry = {
            "source_term": "hello", "target_term": "bonjour",
            "has_context": True, "context_data": '["val1", "val2"]',
            "usage_notes": None,
        }
        line = ContextAwarePromptBuilder.render_entry(entry)
        # Should use raw string since parsed JSON is not a dict
        assert '["val1", "val2"]' in line


class TestComputeContextSimilarity:
    """ContextAwarePromptBuilder.compute_context_similarity."""

    def test_exact_match(self):
        """Identical contexts => 1.0."""
        ctx = {"a": "hello", "b": "world"}
        sim = ContextAwarePromptBuilder.compute_context_similarity(ctx, ctx)
        assert sim == 1.0

    def test_no_match(self):
        """Disjoint contexts => 0.0."""
        ctx1 = {"a": "hello"}
        ctx2 = {"b": "world"}
        sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
        assert sim == 0.0

    def test_partial_match(self):
        """Partial overlap yields value between 0 and 1."""
        ctx1 = {"a": "hello", "b": "world"}
        ctx2 = {"a": "hello", "c": "foo"}
        sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
        # intersection = {"hello"}, union = {"hello", "world", "foo"} => 1/3
        assert 0.33 < sim < 0.34

    def test_none_entry_context(self):
        """None entry_context => 0.0."""
        sim = ContextAwarePromptBuilder.compute_context_similarity(None, {"a": "b"})
        assert sim == 0.0

    def test_none_row_context(self):
        """None row_context => 0.0."""
        sim = ContextAwarePromptBuilder.compute_context_similarity({"a": "b"}, None)
        assert sim == 0.0

    def test_empty_values(self):
        """Context with all None values => 0.0."""
        ctx1 = {"a": None, "b": None}
        ctx2 = {"a": "hello"}
        sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
        assert sim == 0.0

    def test_case_insensitive(self):
        """Values are lowercased."""
        ctx1 = {"a": "Hello"}
        ctx2 = {"a": "hello"}
        sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
        assert sim == 1.0


class TestBuildContextEntries:
    """ContextAwarePromptBuilder.build_context_entries."""

    def test_basic_build(self):
        """Build list of rendered entries sorted by priority."""
        entries = [
            {"source_term": "hello", "target_term": "bonjour",
             "has_context": False, "context_data": None, "usage_notes": None},
            {"source_term": "world", "target_term": "monde",
             "has_context": True, "context_data": {"table": "users"},
             "usage_notes": None},
        ]
        result = ContextAwarePromptBuilder.build_context_entries(entries)
        assert len(result) == 2

    def test_priority_by_similarity(self):
        """Entries matching row_context get priority prefix."""
        entries = [
            {"source_term": "hello", "target_term": "bonjour",
             "has_context": True, "context_data": {"table": "users"},
             "usage_notes": None},
            {"source_term": "world", "target_term": "monde",
             "has_context": True, "context_data": {"table": "orders"},
             "usage_notes": None},
        ]
        row_context = {"table": "users"}
        result = ContextAwarePromptBuilder.build_context_entries(entries, row_context)
        # First entry matches and gets priority
        assert "# PRIORITY" in result[0]
        assert "# PRIORITY" not in result[1]

    def test_no_row_context(self):
        """No row_context => no priority."""
        entries = [
            {"source_term": "hello", "target_term": "bonjour",
             "has_context": False, "context_data": None, "usage_notes": None},
        ]
        result = ContextAwarePromptBuilder.build_context_entries(entries)
        assert len(result) == 1
        assert "# PRIORITY" not in result[0]

    def test_empty_entries(self):
        """Empty entries returns empty list."""
        result = ContextAwarePromptBuilder.build_context_entries([])
        assert result == []

    def test_object_entries(self):
        """Entries as objects work too."""
        entry = MagicMock()
        entry.source_term = "hello"
        entry.target_term = "bonjour"
        entry.has_context = True
        entry.context_data = {"table": "users"}
        entry.usage_notes = None

        result = ContextAwarePromptBuilder.build_context_entries([entry], {"table": "users"})
        assert len(result) == 1
        assert "# PRIORITY" in result[0]
# #endregion Test.ContextAwarePromptBuilder
