#!/usr/bin/env python3
"""
Test DSL notation searches against v7 Qdrant collection.

Verifies that symbol-enriched relationship_text enables semantic matching
of DSL patterns like Ƀ[ᵬ×2] to ButtonList with 2 Buttons.

FINDINGS:
---------
1. Text search on relationship_text works for:
   - Symbol + name queries: "§ Section", "ᵬ Button"
   - DSL patterns: "Ƀ[ᵬ]", "ℊ[ǵ"

2. Semantic search on relationships vector (MiniLM) works for:
   - Natural language: "ButtonList contains Button" (0.909 score)
   - Symbol + NL: "ᵬ Button" (0.634 score)
   - DO NOT query with symbols alone - they will be ignored

3. DSL-annotated card_params (e.g., "δ:status": "All systems operational"):
   - Use StructureValidator.reverse_symbols for symbol → component mapping
   - Query the `inputs` vector (ColBERT) with the VALUES, not symbols
   - DSL keys are for SLOT MAPPING only, not querying

4. NEVER hardcode DSL symbol mappings - use StructureValidator
"""

import os
import sys

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from gchat.card_framework_wrapper import get_card_framework_wrapper


def test_dsl_searches():
    """Test various DSL notation searches."""
    print("=" * 60)
    print("DSL NOTATION SEARCH TESTS")
    print("=" * 60)

    # Get wrapper with v7 collection
    wrapper = get_card_framework_wrapper()
    collection = wrapper.collection_name
    print(f"\nUsing collection: {collection}")

    # DSL test queries
    dsl_queries = [
        # Symbol-only queries
        ("ᵬ", "Button symbol"),
        ("Ƀ", "ButtonList symbol"),
        ("§", "Section symbol"),
        ("δ", "DecoratedText symbol"),
        ("ℊ", "Grid symbol"),
        # DSL structure patterns
        ("Ƀ[ᵬ]", "ButtonList containing Button"),
        ("Ƀ[ᵬ×2]", "ButtonList with 2 Buttons"),
        ("§[δ]", "Section with DecoratedText"),
        ("§[δ, Ƀ[ᵬ]]", "Section with DecoratedText and ButtonList"),
        # Mixed queries (DSL + natural language)
        ("ᵬ Button", "Button with symbol"),
        ("Ƀ ButtonList contains Button", "ButtonList description"),
        ("ℊ Grid items", "Grid with items"),
    ]

    print("\n" + "-" * 60)
    print("Testing search_by_relationship (MiniLM on 'relationships' vector)")
    print("-" * 60)

    for query, description in dsl_queries:
        print(f"\n🔍 Query: '{query}' ({description})")
        results = wrapper.search_by_relationship(query, limit=3)

        if results:
            for i, r in enumerate(results, 1):
                name = r.get("name", "?")
                score = r.get("score", 0)
                rel_text = r.get("relationship_text", "")[:80]
                child_classes = r.get("child_classes", [])
                print(f"   {i}. {name} (score: {score:.3f})")
                if rel_text:
                    print(f"      relationship_text: {rel_text}...")
                if child_classes:
                    print(f"      children: {child_classes[:5]}")
        else:
            print("   ❌ No results")

    # Test a few with the regular search method for comparison
    print("\n" + "-" * 60)
    print("Comparison: Regular semantic search")
    print("-" * 60)

    comparison_queries = ["Button", "ButtonList", "Section", "Grid"]
    for query in comparison_queries:
        print(f"\n🔍 Query: '{query}'")
        results = wrapper.search(query, limit=2)
        for i, r in enumerate(results, 1):
            print(
                f"   {i}. {r.get('name', r.get('path', '?'))} (score: {r.get('score', 0):.3f})"
            )

    # Direct Qdrant query to verify relationship_text content
    print("\n" + "-" * 60)
    print("Verifying relationship_text in collection")
    print("-" * 60)

    if wrapper.client:
        # Get a few points and show their relationship_text
        from qdrant_client.models import FieldCondition, Filter, MatchValue

        # Get Button specifically
        try:
            results = wrapper.client.scroll(
                collection_name=collection,
                scroll_filter=Filter(
                    must=[FieldCondition(key="name", match=MatchValue(value="Button"))]
                ),
                limit=1,
                with_payload=True,
            )
            points, _ = results
            if points:
                p = points[0]
                payload = p.payload
                print(f"\n✓ Button point found (id: {p.id})")
                print(f"  name: {payload.get('name')}")
                print(
                    f"  relationship_text: {payload.get('relationship_text', 'EMPTY')}"
                )
                relationships = payload.get("relationships", {})
                print(
                    f"  relationships.compact_text: {relationships.get('compact_text', 'EMPTY')}"
                )
                print(
                    f"  relationships.symbol_enriched: {relationships.get('symbol_enriched')}"
                )
                print(
                    f"  relationships.child_classes: {relationships.get('child_classes', [])}"
                )
        except Exception as e:
            print(f"❌ Error querying Button: {e}")

        # Get ButtonList specifically
        try:
            results = wrapper.client.scroll(
                collection_name=collection,
                scroll_filter=Filter(
                    must=[
                        FieldCondition(key="name", match=MatchValue(value="ButtonList"))
                    ]
                ),
                limit=1,
                with_payload=True,
            )
            points, _ = results
            if points:
                p = points[0]
                payload = p.payload
                print(f"\n✓ ButtonList point found (id: {p.id})")
                print(f"  name: {payload.get('name')}")
                print(
                    f"  relationship_text: {payload.get('relationship_text', 'EMPTY')}"
                )
                relationships = payload.get("relationships", {})
                print(
                    f"  relationships.compact_text: {relationships.get('compact_text', 'EMPTY')}"
                )
                print(
                    f"  relationships.symbol_enriched: {relationships.get('symbol_enriched')}"
                )
                print(
                    f"  relationships.child_classes: {relationships.get('child_classes', [])}"
                )
        except Exception as e:
            print(f"❌ Error querying ButtonList: {e}")

    # Test DSL card_params parsing
    print("\n" + "-" * 60)
    print("Testing DSL card_params parsing")
    print("-" * 60)

    from gchat.card_builder import get_smart_card_builder

    builder = get_smart_card_builder()

    card_params = {
        "δ:status": "All systems operational",
        "δ:metric": "99.9% uptime",
        "ᵬ:1": "Refresh",
        "ᵬ:2": "Details | https://example.com",
        "title": "Dashboard",
        "subtitle": "System Status",
    }

    regular, dsl_slots = builder._parse_dsl_card_params(card_params)

    print(f"\n✓ Regular params: {list(regular.keys())}")
    print(f"✓ DSL slots parsed: {len(dsl_slots)}")
    for key, info in dsl_slots.items():
        print(f"  {key} → {info['component']}[{info['slot_name']}]")

    # Search by content values
    patterns = builder._search_patterns_by_content_values(dsl_slots, limit=3)
    print(f"\n✓ Found {len(patterns)} matching patterns via inputs vector:")
    for p in patterns[:2]:
        print(f"  - {p.get('name')}: score={p.get('score', 0):.2f}")

    print("\n" + "=" * 60)
    print("TEST COMPLETE")
    print("=" * 60)


def test_extract_dsl_from_text():
    """Test the new extract_dsl_from_text method."""
    print("\n" + "=" * 60)
    print("TEST: extract_dsl_from_text()")
    print("=" * 60)

    wrapper = get_card_framework_wrapper()

    test_cases = [
        # DSL at start
        "§[δ, Ƀ[ᵬ×2]] Build a status card with buttons",
        # DSL with pipe separator
        "§[δ] | DecoratedText :: Simple text card",
        # Complex nested DSL
        "§[Ȼ[Ɔ[δ], Ɔ[δ]]] Two column layout",
        # No DSL - plain text
        "Build a card with action buttons",
        # Inline symbols (not structured DSL)
        "Use δ for text and ᵬ for buttons",
    ]

    for text in test_cases:
        result = wrapper.extract_dsl_from_text(text)
        print(f"\n🔍 Input: {text[:55]}...")
        print(f"   has_dsl: {result['has_dsl']}")
        if result["has_dsl"]:
            print(f"   dsl: {result['dsl']}")
            print(f"   description: {result['description'][:40]}...")
            comp_names = [c["name"] for c in result["components"]]
            print(f"   components: {comp_names}")
        else:
            if result.get("inline_symbols"):
                print(f"   inline_symbols: {result['inline_symbols'][:3]}")


def test_search_by_dsl():
    """Test the new search_by_dsl method with ColBERT."""
    print("\n" + "=" * 60)
    print("TEST: search_by_dsl() with ColBERT vectors")
    print("=" * 60)

    wrapper = get_card_framework_wrapper()

    test_queries = [
        ("§[δ] Simple text card", "components", "class"),
        ("§[δ, Ƀ[ᵬ×2]] Card with buttons", "components", "class"),
        ("§[δ, Ƀ[ᵬ×2]] Card with buttons", "inputs", "instance_pattern"),
    ]

    for query, vector_name, type_filter in test_queries:
        print(f"\n🔍 Query: {query[:45]}...")
        print(f"   Vector: {vector_name}, Filter: {type_filter}")

        results = wrapper.search_by_dsl(
            text=query,
            limit=5,
            vector_name=vector_name,
            type_filter=type_filter,
            token_ratio=1.0,
        )

        if results:
            for r in results[:3]:
                print(f"   ✓ {r['name']:<25} score={r['score']:.3f} type={r['type']}")
        else:
            print("   ❌ No results")


def test_search_by_dsl_with_truncation():
    """Test token truncation optimization."""
    print("\n" + "=" * 60)
    print("TEST: search_by_dsl() with token truncation")
    print("=" * 60)

    import time

    wrapper = get_card_framework_wrapper()
    query = "§[δ, Ƀ[ᵬ×2]] Build a status card with action buttons"

    print(f"\nQuery: {query}")
    print("-" * 50)

    baseline_names = None
    for ratio in [1.0, 0.5, 0.25]:
        start = time.perf_counter()
        results = wrapper.search_by_dsl(
            text=query,
            limit=5,
            vector_name="components",
            type_filter="class",
            token_ratio=ratio,
        )
        elapsed_ms = (time.perf_counter() - start) * 1000

        result_names = [r["name"] for r in results[:5]]

        if ratio == 1.0:
            baseline_names = result_names
            overlap = "baseline"
        else:
            overlap_count = len(set(result_names) & set(baseline_names or []))
            overlap = f"{overlap_count}/5 overlap"

        print(
            f"   {ratio:.0%} tokens: {elapsed_ms:6.1f}ms, {len(results)} results ({overlap})"
        )


def test_search_by_dsl_hybrid():
    """Test hybrid search returning both classes and patterns."""
    print("\n" + "=" * 60)
    print("TEST: search_by_dsl_hybrid()")
    print("=" * 60)

    wrapper = get_card_framework_wrapper()
    query = "§[δ, Ƀ[ᵬ×2]] Status indicator with action buttons"

    print(f"\nQuery: {query}")

    results = wrapper.search_by_dsl_hybrid(
        text=query,
        limit=5,
        token_ratio=0.5,
    )

    qi = results["query_info"]
    print(f"\n📋 Query Info:")
    print(f"   DSL: {qi.get('dsl')}")
    print(f"   Description: {qi.get('description')[:40]}...")

    print(f"\n📦 Class Results ({len(results['classes'])}):")
    for r in results["classes"][:3]:
        print(f"   {r['name']:<25} score={r['score']:.3f}")

    print(f"\n📄 Pattern Results ({len(results['patterns'])}):")
    for r in results["patterns"][:3]:
        desc = r.get("card_description", "")[:35]
        print(f"   {r['name']:<25} score={r['score']:.3f} - {desc}...")


def test_search_hybrid_with_feedback_filters():
    """Test search_hybrid with content/form feedback filters."""
    print("\n" + "=" * 60)
    print("TEST: search_hybrid() with feedback filters")
    print("=" * 60)

    wrapper = get_card_framework_wrapper()

    # Test with positive content feedback filter
    print("\n🔍 Testing with content_feedback='positive'...")
    classes, patterns, rels = wrapper.search_hybrid(
        description="A status card with buttons",
        limit=5,
        content_feedback="positive",
        form_feedback="positive",
    )

    print(
        f"   With filters: {len(classes)} classes, {len(patterns)} patterns, {len(rels)} relationships"
    )

    # Show pattern feedback values
    for i, p in enumerate(patterns[:3], 1):
        content_fb = p.get("content_feedback", "none")
        form_fb = p.get("form_feedback", "none")
        print(
            f"   {i}. {p.get('name', '?')[:30]} - content={content_fb}, form={form_fb}"
        )

    # Test without feedback filters for comparison
    print("\n🔍 Testing without feedback filters...")
    classes2, patterns2, rels2 = wrapper.search_hybrid(
        description="A status card with buttons",
        limit=5,
    )

    print(
        f"   Without filters: {len(classes2)} classes, {len(patterns2)} patterns, {len(rels2)} relationships"
    )

    # Compare counts
    print("\n📊 Comparison:")
    print(f"   With feedback filters: {len(patterns)} patterns")
    print(f"   Without filters: {len(patterns2)} patterns")
    if len(patterns2) > len(patterns):
        print("   ✓ More results without filters (expected)")


def test_search_patterns_for_card():
    """Test the new search_patterns_for_card convenience function."""
    print("\n" + "=" * 60)
    print("TEST: search_patterns_for_card()")
    print("=" * 60)

    from gchat.card_framework_wrapper import search_patterns_for_card

    # Test with DSL
    print("\n🔍 Testing with DSL notation...")
    result = search_patterns_for_card("§[δ, Ƀ[ᵬ×2]] Build a card")
    print(f"   has_dsl: {result['has_dsl']}")
    print(f"   dsl: {result.get('dsl', 'N/A')}")
    print(f"   patterns found: {len(result.get('patterns', []))}")
    print(f"   classes found: {len(result.get('classes', []))}")

    assert result["has_dsl"] is True, "Expected has_dsl=True with DSL notation"
    assert result["dsl"] == "§[δ, Ƀ[ᵬ×2]]", f"DSL mismatch: {result['dsl']}"

    # Test without DSL
    print("\n🔍 Testing without DSL notation...")
    result2 = search_patterns_for_card("A card with decorated text")
    print(f"   has_dsl: {result2['has_dsl']}")
    print(f"   patterns found: {len(result2.get('patterns', []))}")
    print(f"   classes found: {len(result2.get('classes', []))}")

    assert result2["has_dsl"] is False, "Expected has_dsl=False without DSL notation"

    # Test with require_positive_feedback=False
    print("\n🔍 Testing with require_positive_feedback=False...")
    result3 = search_patterns_for_card(
        "A status indicator card",
        require_positive_feedback=False,
    )
    print(f"   has_dsl: {result3['has_dsl']}")
    print(f"   patterns found: {len(result3.get('patterns', []))}")

    print("\n✓ search_patterns_for_card tests passed")


def test_search_v7_include_classes_flag():
    """Test include_classes parameter."""
    print("\n" + "=" * 60)
    print("TEST: search_hybrid() with include_classes flag")
    print("=" * 60)

    wrapper = get_card_framework_wrapper()

    # With classes
    print("\n🔍 Testing with include_classes=True...")
    classes, patterns, _ = wrapper.search_hybrid(
        description="Button widget",
        include_classes=True,
        limit=5,
    )
    print(f"   Classes returned: {len(classes)}")
    for c in classes[:3]:
        print(f"   - {c.get('name', '?')} (type={c.get('type', '?')})")

    # Without classes
    print("\n🔍 Testing with include_classes=False...")
    classes2, patterns2, _ = wrapper.search_hybrid(
        description="Button widget",
        include_classes=False,
        limit=5,
    )
    print(f"   Classes returned: {len(classes2)}")

    # Verify the flag works
    print("\n📊 Comparison:")
    print(f"   include_classes=True: {len(classes)} classes")
    print(f"   include_classes=False: {len(classes2)} classes")

    assert len(classes) > 0, "Expected classes with include_classes=True"
    assert len(classes2) == 0, (
        f"Expected 0 classes with include_classes=False, got {len(classes2)}"
    )

    print("\n✓ include_classes flag tests passed")


if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1 and sys.argv[1] == "--new":
        # Test only the new methods
        print("Testing NEW ModuleWrapper DSL search methods")
        print("=" * 60)
        test_extract_dsl_from_text()
        test_search_by_dsl()
        test_search_by_dsl_with_truncation()
        test_search_by_dsl_hybrid()
        # New tests for feedback filters and convenience functions
        test_search_hybrid_with_feedback_filters()
        test_search_patterns_for_card()
        test_search_v7_include_classes_flag()
        print("\n✅ New method tests complete!")
    else:
        # Run original tests
        test_dsl_searches()
        # Also run new tests
        test_extract_dsl_from_text()
        test_search_by_dsl()
        test_search_by_dsl_with_truncation()
        test_search_by_dsl_hybrid()
        # New tests for feedback filters and convenience functions
        test_search_hybrid_with_feedback_filters()
        test_search_patterns_for_card()
        test_search_v7_include_classes_flag()
