"""
Tests for the friendly naming service.
"""

import re
import pytest
from pathlib import Path


# Copy the pure functions from naming_service for testing
# This avoids import issues with the app module

CROCKFORD_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz"


def normalize_name(name: str) -> str:
    """Copy of normalize_name for testing."""
    name = name.lower()
    name = re.sub(r"[\s_]+", "-", name)
    name = re.sub(r"[^a-z0-9-]", "", name)
    name = re.sub(r"-+", "-", name)
    name = name.strip("-")
    return name or "unnamed"


def parse_friendly_name(friendly_name: str):
    """Copy of parse_friendly_name for testing."""
    parts = friendly_name.strip().split(" ", 1)
    base_name = parts[0]
    version_id = parts[1] if len(parts) > 1 else None
    return base_name, version_id


def format_friendly_name(base_name: str, version_id: str) -> str:
    """Copy of format_friendly_name for testing."""
    return f"{base_name} {version_id}"


def format_l2_name(actor_id: str, base_name: str, version_id: str) -> str:
    """Copy of format_l2_name for testing."""
    return f"{actor_id} {base_name} {version_id}"


class TestNameNormalization:
    """Tests for name normalization."""

    def test_normalize_simple_name(self) -> None:
        """Simple names should be lowercased."""
        assert normalize_name("Brightness") == "brightness"

    def test_normalize_spaces_to_dashes(self) -> None:
        """Spaces should be converted to dashes."""
        assert normalize_name("My Cool Effect") == "my-cool-effect"

    def test_normalize_underscores_to_dashes(self) -> None:
        """Underscores should be converted to dashes."""
        assert normalize_name("brightness_v2") == "brightness-v2"

    def test_normalize_removes_special_chars(self) -> None:
        """Special characters should be removed."""
        # Special chars are removed (not replaced with dashes)
        assert normalize_name("Test!!!Effect") == "testeffect"
        assert normalize_name("cool@effect#1") == "cooleffect1"
        # But spaces/underscores become dashes first
        assert normalize_name("Test Effect!") == "test-effect"

    def test_normalize_collapses_dashes(self) -> None:
        """Multiple dashes should be collapsed."""
        assert normalize_name("test--effect") == "test-effect"
        assert normalize_name("test___effect") == "test-effect"

    def test_normalize_strips_edge_dashes(self) -> None:
        """Leading/trailing dashes should be stripped."""
        assert normalize_name("-test-effect-") == "test-effect"

    def test_normalize_empty_returns_unnamed(self) -> None:
        """Empty names should return 'unnamed'."""
        assert normalize_name("") == "unnamed"
        assert normalize_name("---") == "unnamed"
        assert normalize_name("!!!") == "unnamed"


class TestFriendlyNameParsing:
    """Tests for friendly name parsing."""

    def test_parse_base_name_only(self) -> None:
        """Parsing base name only returns None for version."""
        base, version = parse_friendly_name("my-effect")
        assert base == "my-effect"
        assert version is None

    def test_parse_with_version(self) -> None:
        """Parsing with version returns both parts."""
        base, version = parse_friendly_name("my-effect 01hw3x9k")
        assert base == "my-effect"
        assert version == "01hw3x9k"

    def test_parse_strips_whitespace(self) -> None:
        """Parsing should strip leading/trailing whitespace."""
        base, version = parse_friendly_name("  my-effect  ")
        assert base == "my-effect"
        assert version is None


class TestFriendlyNameFormatting:
    """Tests for friendly name formatting."""

    def test_format_friendly_name(self) -> None:
        """Format combines base and version with space."""
        assert format_friendly_name("my-effect", "01hw3x9k") == "my-effect 01hw3x9k"

    def test_format_l2_name(self) -> None:
        """L2 format includes actor ID."""
        result = format_l2_name("@alice@example.com", "my-effect", "01hw3x9k")
        assert result == "@alice@example.com my-effect 01hw3x9k"


class TestDatabaseSchemaExists:
    """Tests that verify database schema includes friendly_names table."""

    def test_schema_has_friendly_names_table(self) -> None:
        """Database schema should include friendly_names table."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "CREATE TABLE IF NOT EXISTS friendly_names" in content

    def test_schema_has_required_columns(self) -> None:
        """Friendly names table should have required columns."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "actor_id" in content
        assert "base_name" in content
        assert "version_id" in content
        assert "item_type" in content
        assert "display_name" in content

    def test_schema_has_unique_constraints(self) -> None:
        """Friendly names table should have unique constraints."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        # Unique on (actor_id, base_name, version_id)
        assert "UNIQUE(actor_id, base_name, version_id)" in content
        # Unique on (actor_id, cid)
        assert "UNIQUE(actor_id, cid)" in content


class TestDatabaseFunctionsExist:
    """Tests that verify database functions exist."""

    def test_create_friendly_name_exists(self) -> None:
        """create_friendly_name function should exist."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "async def create_friendly_name(" in content

    def test_get_friendly_name_by_cid_exists(self) -> None:
        """get_friendly_name_by_cid function should exist."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "async def get_friendly_name_by_cid(" in content

    def test_resolve_friendly_name_exists(self) -> None:
        """resolve_friendly_name function should exist."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "async def resolve_friendly_name(" in content

    def test_list_friendly_names_exists(self) -> None:
        """list_friendly_names function should exist."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "async def list_friendly_names(" in content

    def test_delete_friendly_name_exists(self) -> None:
        """delete_friendly_name function should exist."""
        path = Path(__file__).parent.parent / "database.py"
        content = path.read_text()
        assert "async def delete_friendly_name(" in content


class TestNamingServiceModuleExists:
    """Tests that verify naming service module structure."""

    def test_module_file_exists(self) -> None:
        """Naming service module file should exist."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        assert path.exists()

    def test_module_has_normalize_name(self) -> None:
        """Module should have normalize_name function."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "def normalize_name(" in content

    def test_module_has_generate_version_id(self) -> None:
        """Module should have generate_version_id function."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "def generate_version_id(" in content

    def test_module_has_naming_service_class(self) -> None:
        """Module should have NamingService class."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "class NamingService:" in content

    def test_naming_service_has_assign_name(self) -> None:
        """NamingService should have assign_name method."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "async def assign_name(" in content

    def test_naming_service_has_resolve(self) -> None:
        """NamingService should have resolve method."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "async def resolve(" in content

    def test_naming_service_has_get_by_cid(self) -> None:
        """NamingService should have get_by_cid method."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "async def get_by_cid(" in content


class TestVersionIdProperties:
    """Tests for version ID format properties (using actual function)."""

    def test_version_id_format(self) -> None:
        """Version ID should use base32-crockford alphabet."""
        # Read the naming service to verify it uses the right alphabet
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert 'CROCKFORD_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz"' in content

    def test_version_id_uses_hmac(self) -> None:
        """Version ID generation should use HMAC for server verification."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "hmac.new(" in content

    def test_version_id_uses_timestamp(self) -> None:
        """Version ID generation should be timestamp-based."""
        path = Path(__file__).parent.parent / "app" / "services" / "naming_service.py"
        content = path.read_text()
        assert "time.time()" in content
