"""Tests for tool name validation utilities (SEP-986)."""

import logging

import pytest

from mcp.shared.tool_name_validation import (
    issue_tool_name_warning,
    validate_and_warn_tool_name,
    validate_tool_name,
)

# Tests for validate_tool_name function - valid names


@pytest.mark.parametrize(
    "tool_name",
    [
        "getUser",
        "get_user_profile",
        "user-profile-update",
        "admin.tools.list",
        "DATA_EXPORT_v2.1",
        "a",
        "a" * 128,
    ],
    ids=[
        "simple_alphanumeric",
        "with_underscores",
        "with_dashes",
        "with_dots",
        "mixed_characters",
        "single_character",
        "max_length_128",
    ],
)
def test_validate_tool_name_accepts_valid_names(tool_name: str) -> None:
    """Valid tool names should pass validation with no warnings."""
    result = validate_tool_name(tool_name)
    assert result.is_valid is True
    assert result.warnings == []


# Tests for validate_tool_name function - invalid names


def test_validate_tool_name_rejects_empty_name() -> None:
    """Empty names should be rejected."""
    result = validate_tool_name("")
    assert result.is_valid is False
    assert "Tool name cannot be empty" in result.warnings


def test_validate_tool_name_rejects_name_exceeding_max_length() -> None:
    """Names exceeding 128 characters should be rejected."""
    result = validate_tool_name("a" * 129)
    assert result.is_valid is False
    assert any("exceeds maximum length of 128 characters (current: 129)" in w for w in result.warnings)


@pytest.mark.parametrize(
    "tool_name,expected_char",
    [
        ("get user profile", "' '"),
        ("get,user,profile", "','"),
        ("user/profile/update", "'/'"),
        ("user@domain.com", "'@'"),
        # a single trailing newline slipped past `$` with re.match
        ("valid_name\n", "'\\n'"),
        ("a" * 127 + "\n", "'\\n'"),
    ],
    ids=[
        "with_spaces",
        "with_commas",
        "with_slashes",
        "with_at_symbol",
        "with_trailing_newline",
        "max_length_with_trailing_newline",
    ],
)
def test_validate_tool_name_rejects_invalid_characters(tool_name: str, expected_char: str) -> None:
    """Names with invalid characters should be rejected."""
    result = validate_tool_name(tool_name)
    assert result.is_valid is False
    assert any("invalid characters" in w and expected_char in w for w in result.warnings)


def test_validate_tool_name_rejects_multiple_invalid_chars() -> None:
    """Names with multiple invalid chars should list all of them."""
    result = validate_tool_name("user name@domain,com")
    assert result.is_valid is False
    warning = next(w for w in result.warnings if "invalid characters" in w)
    assert "' '" in warning
    assert "'@'" in warning
    assert "','" in warning


def test_validate_tool_name_rejects_unicode_characters() -> None:
    """Names with unicode characters should be rejected."""
    result = validate_tool_name("user-\u00f1ame")  # n with tilde
    assert result.is_valid is False


# Tests for validate_tool_name function - warnings for problematic patterns


def test_validate_tool_name_warns_on_leading_dash() -> None:
    """Names starting with dash should generate warning but be valid."""
    result = validate_tool_name("-get-user")
    assert result.is_valid is True
    assert any("starts or ends with a dash" in w for w in result.warnings)


def test_validate_tool_name_warns_on_trailing_dash() -> None:
    """Names ending with dash should generate warning but be valid."""
    result = validate_tool_name("get-user-")
    assert result.is_valid is True
    assert any("starts or ends with a dash" in w for w in result.warnings)


def test_validate_tool_name_warns_on_leading_dot() -> None:
    """Names starting with dot should generate warning but be valid."""
    result = validate_tool_name(".get.user")
    assert result.is_valid is True
    assert any("starts or ends with a dot" in w for w in result.warnings)


def test_validate_tool_name_warns_on_trailing_dot() -> None:
    """Names ending with dot should generate warning but be valid."""
    result = validate_tool_name("get.user.")
    assert result.is_valid is True
    assert any("starts or ends with a dot" in w for w in result.warnings)


# Tests for issue_tool_name_warning function


def test_issue_tool_name_warning_logs_warnings(caplog: pytest.LogCaptureFixture) -> None:
    """Warnings should be logged at WARNING level."""
    warnings = ["Warning 1", "Warning 2"]
    with caplog.at_level(logging.WARNING):
        issue_tool_name_warning("test-tool", warnings)

    assert 'Tool name validation warning for "test-tool"' in caplog.text
    assert "- Warning 1" in caplog.text
    assert "- Warning 2" in caplog.text
    assert "Tool registration will proceed" in caplog.text
    assert "SEP-986" in caplog.text


def test_issue_tool_name_warning_no_logging_for_empty_warnings(caplog: pytest.LogCaptureFixture) -> None:
    """Empty warnings list should not produce any log output."""
    with caplog.at_level(logging.WARNING):
        issue_tool_name_warning("test-tool", [])

    assert caplog.text == ""


# Tests for validate_and_warn_tool_name function


def test_validate_and_warn_tool_name_returns_true_for_valid_name() -> None:
    """Valid names should return True."""
    assert validate_and_warn_tool_name("valid-tool-name") is True


def test_validate_and_warn_tool_name_returns_false_for_invalid_name() -> None:
    """Invalid names should return False."""
    assert validate_and_warn_tool_name("") is False
    assert validate_and_warn_tool_name("a" * 129) is False
    assert validate_and_warn_tool_name("invalid name") is False


def test_validate_and_warn_tool_name_logs_warnings_for_invalid_name(caplog: pytest.LogCaptureFixture) -> None:
    """Invalid names should trigger warning logs."""
    with caplog.at_level(logging.WARNING):
        validate_and_warn_tool_name("invalid name")

    assert "Tool name validation warning" in caplog.text


def test_validate_and_warn_tool_name_no_warnings_for_clean_valid_name(caplog: pytest.LogCaptureFixture) -> None:
    """Clean valid names should not produce any log output."""
    with caplog.at_level(logging.WARNING):
        result = validate_and_warn_tool_name("clean-tool-name")

    assert result is True
    assert caplog.text == ""


# Tests for edge cases


@pytest.mark.parametrize(
    "tool_name,is_valid,expected_warning_fragment",
    [
        ("...", True, "starts or ends with a dot"),
        ("---", True, "starts or ends with a dash"),
        ("///", False, "invalid characters"),
        ("user@name123", False, "invalid characters"),
    ],
    ids=[
        "only_dots",
        "only_dashes",
        "only_slashes",
        "mixed_valid_invalid",
    ],
)
def test_edge_cases(tool_name: str, is_valid: bool, expected_warning_fragment: str) -> None:
    """Various edge cases should be handled correctly."""
    result = validate_tool_name(tool_name)
    assert result.is_valid is is_valid
    assert any(expected_warning_fragment in w for w in result.warnings)
