"""Tests for comparator functions."""

import pytest

from ashr_labs.comparators import (
    strip_markdown,
    tokenize,
    fuzzy_str_match,
    extract_tool_args,
    compare_tool_args,
    text_similarity,
)


class TestStripMarkdown:
    def test_removes_bold(self):
        assert strip_markdown("**bold text**") == "bold text"

    def test_removes_italic(self):
        assert strip_markdown("*italic text*") == "italic text"

    def test_removes_headers(self):
        assert strip_markdown("## Header\nContent") == "Header Content"

    def test_removes_bullets(self):
        assert strip_markdown("- item one\n- item two") == "item one item two"

    def test_removes_links(self):
        assert strip_markdown("[click here](https://example.com)") == "click here"

    def test_collapses_whitespace(self):
        assert strip_markdown("hello   world") == "hello world"

    def test_plain_text_unchanged(self):
        assert strip_markdown("plain text") == "plain text"


class TestTokenize:
    def test_lowercases(self):
        assert tokenize("Hello World") == ["hello", "world"]

    def test_strips_punctuation(self):
        assert tokenize("hello, world!") == ["hello", "world"]

    def test_strips_markdown_then_tokenizes(self):
        assert tokenize("**Bold** and *italic*") == ["bold", "and", "italic"]

    def test_empty_string(self):
        assert tokenize("") == []


class TestFuzzyStrMatch:
    def test_exact_match(self):
        assert fuzzy_str_match("hello", "hello") is True

    def test_case_insensitive(self):
        assert fuzzy_str_match("Hello", "hello") is True

    def test_punctuation_ignored(self):
        assert fuzzy_str_match("hello!", "hello") is True

    def test_containment(self):
        assert fuzzy_str_match("order", "order id") is True

    def test_word_overlap_short_strings(self):
        # Short strings (<=5 words) need only 0.35 overlap
        assert fuzzy_str_match("damaged item refund", "item refund request") is True

    def test_no_overlap(self):
        assert fuzzy_str_match("apple banana", "cherry grape") is False

    def test_custom_threshold(self):
        # Force a high threshold that won't match partial overlap
        assert fuzzy_str_match("one two three", "one four five", threshold=0.9) is False
        # Force a low threshold that will match
        assert fuzzy_str_match("one two three", "one four five", threshold=0.2) is True

    def test_empty_strings(self):
        assert fuzzy_str_match("", "") is True  # both normalize to ""
        assert fuzzy_str_match("hello", "") is True  # "" in "hello"


class TestExtractToolArgs:
    def test_dict_arguments(self):
        tc = {"name": "lookup", "arguments": {"order_id": "ORD-123"}}
        assert extract_tool_args(tc) == {"order_id": "ORD-123"}

    def test_json_string_arguments(self):
        tc = {"name": "lookup", "arguments_json": '{"order_id": "ORD-123"}'}
        assert extract_tool_args(tc) == {"order_id": "ORD-123"}

    def test_dict_preferred_over_json(self):
        tc = {
            "arguments": {"a": 1},
            "arguments_json": '{"b": 2}',
        }
        assert extract_tool_args(tc) == {"a": 1}

    def test_empty_tool_call(self):
        assert extract_tool_args({}) == {}

    def test_invalid_json(self):
        tc = {"arguments_json": "not valid json"}
        assert extract_tool_args(tc) == {}


class TestCompareToolArgs:
    def test_exact_match(self):
        exp = {"arguments": {"order_id": "ORD-123"}}
        act = {"arguments": {"order_id": "ORD-123"}}
        status, notes = compare_tool_args(exp, act)
        assert status == "exact"
        assert notes is None

    def test_partial_match(self):
        exp = {"arguments": {"order_id": "ORD-123", "reason": "damaged"}}
        act = {"arguments": {"order_id": "ORD-123", "reason": "totally different"}}
        status, notes = compare_tool_args(exp, act)
        assert status == "partial"
        assert notes is not None

    def test_mismatch(self):
        exp = {"arguments": {"order_id": "ORD-123"}}
        act = {"arguments": {"order_id": "ORD-999"}}
        status, notes = compare_tool_args(exp, act)
        assert status == "mismatch"

    def test_both_empty(self):
        status, notes = compare_tool_args({}, {})
        assert status == "exact"
        assert notes is None

    def test_fuzzy_string_match_in_args(self):
        exp = {"arguments": {"reason": "customer wants a refund"}}
        act = {"arguments": {"reason": "customer wants refund"}}
        status, notes = compare_tool_args(exp, act)
        assert status == "exact"

    def test_extra_actual_args_still_exact(self):
        exp = {"arguments": {"order_id": "ORD-123"}}
        act = {"arguments": {"order_id": "ORD-123", "extra": "field"}}
        status, notes = compare_tool_args(exp, act)
        assert status == "exact"

    def test_json_format(self):
        exp = {"arguments_json": '{"order_id": "ORD-123"}'}
        act = {"arguments_json": '{"order_id": "ORD-123"}'}
        status, notes = compare_tool_args(exp, act)
        assert status == "exact"


class TestTextSimilarity:
    def test_identical_texts(self):
        score = text_similarity("Hello world", "Hello world")
        assert score == 1.0

    def test_completely_different(self):
        score = text_similarity("apple banana cherry", "dog elephant frog")
        assert score < 0.2

    def test_similar_texts(self):
        score = text_similarity(
            "Your order ORD-123 has shipped and is on the way",
            "Order ORD-123 has been shipped and is in transit",
        )
        assert score > 0.5

    def test_entity_boost(self):
        # Same entities should boost similarity
        base = text_similarity("order shipped", "package delivered")
        with_entities = text_similarity(
            "order ORD-123 shipped $79.99",
            "order ORD-123 delivered $79.99",
        )
        assert with_entities > base

    def test_concept_boost(self):
        # Same domain concepts should boost similarity
        score = text_similarity(
            "Your refund has been processed and credited",
            "The refund was completed and the amount was credited back",
        )
        assert score > 0.3

    def test_empty_text(self):
        assert text_similarity("", "hello") == 0.0
        assert text_similarity("hello", "") == 0.0
        assert text_similarity("", "") == 0.0

    def test_returns_float_between_0_and_1(self):
        score = text_similarity("some text here", "other text there")
        assert 0.0 <= score <= 1.0

    def test_markdown_stripped(self):
        # Similarity should focus on content, not formatting
        plain = text_similarity("Your order has shipped", "Your order has shipped today")
        markdown = text_similarity(
            "**Your order** has *shipped*", "Your order has shipped today"
        )
        # Markdown version should score similarly since formatting is stripped
        assert abs(plain - markdown) < 0.15
