"""
Tests for LIKE pattern escaping (H-4, #1181).
"""

from __future__ import annotations

import pytest
import pytest_asyncio
import aiosqlite

from cortex.hippo.store.utils import escape_like_pattern
from cortex.hippo.store.conversation_store import ConversationStore
from cortex.hippo.store.summary_store import SummaryStore
from cortex.hippo.tools.grep import execute_grep
from cortex.hippo.tools.conversation_scope import LcmConversationScope


# ── Unit tests for escape_like_pattern ──────────────────────────────


def test_escape_percent():
    """% in pattern should not act as wildcard."""
    assert escape_like_pattern("100%") == "100\\%"
    assert escape_like_pattern("%foo%") == "\\%foo\\%"


def test_escape_underscore():
    """_ in pattern should not act as single-char wildcard."""
    assert escape_like_pattern("foo_bar") == "foo\\_bar"
    assert escape_like_pattern("__init__") == "\\_\\_init\\_\\_"


def test_escape_backslash():
    """\\ in pattern should be literal."""
    assert escape_like_pattern("a\\b") == "a\\\\b"
    # Combined: backslash + percent
    assert escape_like_pattern("\\%") == "\\\\\\%"


def test_normal_pattern_unaffected():
    """Regular text without metacharacters passes through unchanged."""
    assert escape_like_pattern("hello world") == "hello world"
    assert escape_like_pattern("foo123") == "foo123"
    assert escape_like_pattern("") == ""


# ── Integration: pattern length limit in grep tool ──────────────────


@pytest.mark.asyncio
async def test_pattern_length_limit():
    """>500 char patterns should return error from grep tool."""
    # We need a minimal retrieval engine mock
    long_pattern = "a" * 501
    scope = LcmConversationScope(conversation_id=1, all_conversations=False)

    # execute_grep should reject before hitting retrieval
    result = await execute_grep(
        retrieval=None,  # type: ignore — should not be reached
        params={"pattern": long_pattern},
        conversation_scope=scope,
    )
    # Should have error in content
    content = result.get("content", [])
    assert any("Pattern too long" in str(c) for c in content), f"Expected error, got: {result}"


# ── Integration: LIKE escaping through store ────────────────────────


@pytest_asyncio.fixture
async def db():
    """In-memory DB with hippo tables."""
    conn = await aiosqlite.connect(":memory:")
    await conn.execute("""
        CREATE TABLE hippo_conversations (
            conversation_id INTEGER PRIMARY KEY AUTOINCREMENT,
            owner_id TEXT NOT NULL,
            session_id TEXT,
            title TEXT,
            bootstrapped_at TEXT,
            created_at TEXT DEFAULT (datetime('now')),
            updated_at TEXT DEFAULT (datetime('now')),
            session_key TEXT
        )
    """)
    await conn.execute("""
        CREATE TABLE hippo_messages (
            message_id INTEGER PRIMARY KEY AUTOINCREMENT,
            conversation_id INTEGER NOT NULL,
            owner_id TEXT NOT NULL,
            seq INTEGER NOT NULL,
            role TEXT NOT NULL,
            content TEXT NOT NULL,
            token_count INTEGER DEFAULT 0,
            metadata TEXT,
            created_at TEXT DEFAULT (datetime('now'))
        )
    """)
    await conn.commit()
    yield conn
    await conn.close()


@pytest.mark.asyncio
async def test_escape_in_grep_tool(db: aiosqlite.Connection):
    """End-to-end: searching for literal '%' or '_' finds exact matches only."""
    store = ConversationStore(db)

    # Create a conversation
    conv = await store.get_or_create_conversation("owner1", "sess1")

    # Insert messages — one with literal %, one without
    await store.create_message(conv.conversation_id, "owner1", "user", "price is 100% off")
    await store.create_message(conv.conversation_id, "owner1", "user", "price is 1000 off")
    await store.create_message(conv.conversation_id, "owner1", "user", "foo_bar_baz")
    await store.create_message(conv.conversation_id, "owner1", "user", "fooXbarXbaz")

    # Search for literal "100%" — should only match the first message
    results = await store.search_messages("owner1", "100%", mode="regex", conversation_id=conv.conversation_id)
    assert len(results) == 1
    assert "100% off" in results[0].content

    # Search for literal "foo_bar" — should only match foo_bar_baz, not fooXbarXbaz
    results = await store.search_messages("owner1", "foo_bar", mode="regex", conversation_id=conv.conversation_id)
    assert len(results) == 1
    assert "foo_bar" in results[0].content
