"""
Tests for Placeholder Metadata System

Tests the normative standard implementation for placeholder metadata.
"""
import sys
from pathlib import Path

# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))

import pytest
from placeholder_metadata import (
    PlaceholderMetadata,
    PlaceholderMetadataRegistry,
    PlaceholderType,
    TimeWindow,
    OutputType,
    SourceInfo,
    MissingValuePolicy,
    ExceptionHandling,
    validate_metadata,
    ValidationViolation
)


# ── Test Fixtures ─────────────────────────────────────────────────────────────

@pytest.fixture
def valid_metadata():
    """Create a valid metadata instance."""
    return PlaceholderMetadata(
        key="test_placeholder",
        placeholder="{{test_placeholder}}",
        category="Test",
        type=PlaceholderType.ATOMIC,
        description="Test placeholder",
        semantic_contract="A test placeholder for validation",
        unit="kg",
        time_window=TimeWindow.LATEST,
        output_type=OutputType.NUMBER,
        format_hint="85.0 kg",
        example_output="85.0 kg",
        source=SourceInfo(
            resolver="test_resolver",
            module="placeholder_resolver.py",
            source_tables=["test_table"]
        ),
        dependencies=["profile_id"],
        version="1.0.0",
        deprecated=False
    )


@pytest.fixture
def invalid_metadata():
    """Create an invalid metadata instance."""
    return PlaceholderMetadata(
        key="",  # Invalid: empty key
        placeholder="{{}}",
        category="",  # Invalid: empty category
        type=PlaceholderType.LEGACY_UNKNOWN,  # Warning: should be resolved
        description="",  # Invalid: empty description
        semantic_contract="",  # Invalid: empty semantic_contract
        unit=None,
        time_window=TimeWindow.UNKNOWN,  # Warning: should be resolved
        output_type=OutputType.UNKNOWN,  # Warning: should be resolved
        format_hint=None,
        example_output=None,
        source=SourceInfo(
            resolver="unknown"  # Error: resolver must be specified
        ),
        version="1.0.0",
        deprecated=False
    )


# ── Validation Tests ──────────────────────────────────────────────────────────

def test_valid_metadata_passes_validation(valid_metadata):
    """Valid metadata should pass all validation checks."""
    violations = validate_metadata(valid_metadata)
    errors = [v for v in violations if v.severity == "error"]
    assert len(errors) == 0, f"Unexpected errors: {errors}"


def test_invalid_metadata_fails_validation(invalid_metadata):
    """Invalid metadata should fail validation."""
    violations = validate_metadata(invalid_metadata)
    errors = [v for v in violations if v.severity == "error"]
    assert len(errors) > 0, "Expected validation errors"


def test_empty_key_violation(invalid_metadata):
    """Empty key should trigger violation."""
    violations = validate_metadata(invalid_metadata)
    key_violations = [v for v in violations if v.field == "key"]
    assert len(key_violations) > 0


def test_legacy_unknown_type_warning(invalid_metadata):
    """LEGACY_UNKNOWN type should trigger warning."""
    violations = validate_metadata(invalid_metadata)
    type_warnings = [v for v in violations if v.field == "type" and v.severity == "warning"]
    assert len(type_warnings) > 0


def test_unknown_time_window_warning(invalid_metadata):
    """UNKNOWN time window should trigger warning."""
    violations = validate_metadata(invalid_metadata)
    tw_warnings = [v for v in violations if v.field == "time_window" and v.severity == "warning"]
    assert len(tw_warnings) > 0


def test_deprecated_without_replacement_warning():
    """Deprecated placeholder without replacement should trigger warning."""
    metadata = PlaceholderMetadata(
        key="old_placeholder",
        placeholder="{{old_placeholder}}",
        category="Test",
        type=PlaceholderType.ATOMIC,
        description="Deprecated placeholder",
        semantic_contract="Old placeholder",
        unit=None,
        time_window=TimeWindow.LATEST,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="old_resolver"),
        deprecated=True,  # Deprecated
        replacement=None  # No replacement
    )

    violations = validate_metadata(metadata)
    replacement_warnings = [v for v in violations if v.field == "replacement"]
    assert len(replacement_warnings) > 0


# ── Registry Tests ────────────────────────────────────────────────────────────

def test_registry_registration(valid_metadata):
    """Test registering metadata in registry."""
    registry = PlaceholderMetadataRegistry()
    registry.register(valid_metadata, validate=False)

    assert registry.count() == 1
    assert registry.get("test_placeholder") is not None


def test_registry_validation_rejects_invalid():
    """Registry should reject invalid metadata when validation is enabled."""
    registry = PlaceholderMetadataRegistry()

    invalid = PlaceholderMetadata(
        key="",  # Invalid
        placeholder="{{}}",
        category="",
        type=PlaceholderType.ATOMIC,
        description="",
        semantic_contract="",
        unit=None,
        time_window=TimeWindow.LATEST,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="unknown")
    )

    with pytest.raises(ValueError):
        registry.register(invalid, validate=True)


def test_registry_get_by_category(valid_metadata):
    """Test retrieving metadata by category."""
    registry = PlaceholderMetadataRegistry()

    # Create multiple metadata in different categories
    meta1 = valid_metadata
    meta2 = PlaceholderMetadata(
        key="test2",
        placeholder="{{test2}}",
        category="Test",
        type=PlaceholderType.ATOMIC,
        description="Test 2",
        semantic_contract="Test",
        unit=None,
        time_window=TimeWindow.LATEST,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="test2_resolver")
    )
    meta3 = PlaceholderMetadata(
        key="test3",
        placeholder="{{test3}}",
        category="Other",
        type=PlaceholderType.ATOMIC,
        description="Test 3",
        semantic_contract="Test",
        unit=None,
        time_window=TimeWindow.LATEST,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="test3_resolver")
    )

    registry.register(meta1, validate=False)
    registry.register(meta2, validate=False)
    registry.register(meta3, validate=False)

    by_category = registry.get_by_category()
    assert "Test" in by_category
    assert "Other" in by_category
    assert len(by_category["Test"]) == 2
    assert len(by_category["Other"]) == 1


def test_registry_get_by_type(valid_metadata):
    """Test retrieving metadata by type."""
    registry = PlaceholderMetadataRegistry()

    atomic_meta = valid_metadata
    interpreted_meta = PlaceholderMetadata(
        key="interpreted_test",
        placeholder="{{interpreted_test}}",
        category="Test",
        type=PlaceholderType.INTERPRETED,
        description="Interpreted test",
        semantic_contract="Test",
        unit=None,
        time_window=TimeWindow.DAYS_7,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="interpreted_resolver")
    )

    registry.register(atomic_meta, validate=False)
    registry.register(interpreted_meta, validate=False)

    atomic_placeholders = registry.get_by_type(PlaceholderType.ATOMIC)
    interpreted_placeholders = registry.get_by_type(PlaceholderType.INTERPRETED)

    assert len(atomic_placeholders) == 1
    assert len(interpreted_placeholders) == 1


def test_registry_get_deprecated():
    """Test retrieving deprecated placeholders."""
    registry = PlaceholderMetadataRegistry()

    deprecated_meta = PlaceholderMetadata(
        key="deprecated_test",
        placeholder="{{deprecated_test}}",
        category="Test",
        type=PlaceholderType.ATOMIC,
        description="Deprecated",
        semantic_contract="Old placeholder",
        unit=None,
        time_window=TimeWindow.LATEST,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="deprecated_resolver"),
        deprecated=True,
        replacement="{{new_test}}"
    )

    active_meta = PlaceholderMetadata(
        key="active_test",
        placeholder="{{active_test}}",
        category="Test",
        type=PlaceholderType.ATOMIC,
        description="Active",
        semantic_contract="Active placeholder",
        unit=None,
        time_window=TimeWindow.LATEST,
        output_type=OutputType.STRING,
        format_hint=None,
        example_output=None,
        source=SourceInfo(resolver="active_resolver"),
        deprecated=False
    )

    registry.register(deprecated_meta, validate=False)
    registry.register(active_meta, validate=False)

    deprecated = registry.get_deprecated()
    assert len(deprecated) == 1
    assert deprecated[0].key == "deprecated_test"


# ── Serialization Tests ───────────────────────────────────────────────────────

def test_metadata_to_dict(valid_metadata):
    """Test converting metadata to dictionary."""
    data = valid_metadata.to_dict()

    assert isinstance(data, dict)
    assert data['key'] == "test_placeholder"
    assert data['type'] == "atomic"  # Enum converted to string
    assert data['time_window'] == "latest"
    assert data['output_type'] == "number"


def test_metadata_to_json(valid_metadata):
    """Test converting metadata to JSON string."""
    import json

    json_str = valid_metadata.to_json()
    data = json.loads(json_str)

    assert data['key'] == "test_placeholder"
    assert data['type'] == "atomic"


# ── Normative Standard Compliance ─────────────────────────────────────────────

def test_all_mandatory_fields_present(valid_metadata):
    """Test that all mandatory fields from normative standard are present."""
    mandatory_fields = [
        'key', 'placeholder', 'category', 'type', 'description',
        'semantic_contract', 'unit', 'time_window', 'output_type',
        'source', 'version', 'deprecated'
    ]

    for field in mandatory_fields:
        assert hasattr(valid_metadata, field), f"Missing mandatory field: {field}"


def test_type_enum_valid_values():
    """Test that PlaceholderType enum has required values."""
    required_types = ['atomic', 'raw_data', 'interpreted', 'legacy_unknown']

    for type_value in required_types:
        assert any(t.value == type_value for t in PlaceholderType), \
            f"Missing required type: {type_value}"


def test_time_window_enum_valid_values():
    """Test that TimeWindow enum has required values."""
    required_windows = ['latest', '7d', '14d', '28d', '30d', '90d', 'custom', 'mixed', 'unknown']

    for window_value in required_windows:
        assert any(w.value == window_value for w in TimeWindow), \
            f"Missing required time window: {window_value}"


def test_output_type_enum_valid_values():
    """Test that OutputType enum has required values."""
    required_types = ['string', 'number', 'integer', 'boolean', 'json', 'markdown', 'date', 'enum', 'unknown']

    for type_value in required_types:
        assert any(t.value == type_value for t in OutputType), \
            f"Missing required output type: {type_value}"


# ── Run Tests ─────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    pytest.main([__file__, "-v"])
