"""Tests for metadata validators."""

import pytest

from apps.core.choices.base import RichChoice
from apps.core.choices.registry import registry
from apps.core.state_machine.validators import (
    MetadataValidator,
    ValidationError,
    ValidationResult,
    ValidationWarning,
    validate_on_registration,
)


@pytest.fixture
def valid_choices():
    """Create valid choices for testing."""
    choices = [
        RichChoice(
            value="pending",
            label="Pending",
            metadata={"can_transition_to": ["approved", "rejected"]},
        ),
        RichChoice(
            value="approved",
            label="Approved",
            metadata={"is_final": True, "can_transition_to": []},
        ),
        RichChoice(
            value="rejected",
            label="Rejected",
            metadata={"is_final": True, "can_transition_to": []},
        ),
    ]
    registry.register("valid_states", choices, domain="test")
    yield choices
    registry.clear_domain("test")


@pytest.fixture
def invalid_transition_choices():
    """Create choices with invalid transition targets."""
    choices = [
        RichChoice(
            value="pending",
            label="Pending",
            metadata={"can_transition_to": ["nonexistent"]},
        ),
    ]
    registry.register("invalid_trans", choices, domain="test")
    yield choices
    registry.clear_domain("test")


@pytest.fixture
def terminal_with_transitions():
    """Create terminal state with outgoing transitions."""
    choices = [
        RichChoice(
            value="final",
            label="Final",
            metadata={"is_final": True, "can_transition_to": ["pending"]},
        ),
        RichChoice(value="pending", label="Pending", metadata={}),
    ]
    registry.register("terminal_trans", choices, domain="test")
    yield choices
    registry.clear_domain("test")


def test_validation_error_creation():
    """Test ValidationError creation."""
    error = ValidationError(code="TEST_ERROR", message="Test message", state="pending")
    assert error.code == "TEST_ERROR"
    assert error.message == "Test message"
    assert error.state == "pending"
    assert "pending" in str(error)


def test_validation_warning_creation():
    """Test ValidationWarning creation."""
    warning = ValidationWarning(code="TEST_WARNING", message="Test warning", state="pending")
    assert warning.code == "TEST_WARNING"
    assert warning.message == "Test warning"


def test_validation_result_add_error():
    """Test adding errors to ValidationResult."""
    result = ValidationResult(is_valid=True)
    result.add_error("ERROR_CODE", "Error message", "pending")
    assert not result.is_valid
    assert len(result.errors) == 1


def test_validation_result_add_warning():
    """Test adding warnings to ValidationResult."""
    result = ValidationResult(is_valid=True)
    result.add_warning("WARNING_CODE", "Warning message")
    assert result.is_valid  # Warnings don't affect validity
    assert len(result.warnings) == 1


def test_validator_initialization(valid_choices):
    """Test validator initialization."""
    validator = MetadataValidator("valid_states", "test")
    assert validator.choice_group == "valid_states"
    assert validator.domain == "test"


def test_validate_choice_group_valid(valid_choices):
    """Test validation passes for valid choice group."""
    validator = MetadataValidator("valid_states", "test")
    result = validator.validate_choice_group()
    assert result.is_valid
    assert len(result.errors) == 0


def test_validate_transitions_valid(valid_choices):
    """Test transition validation passes for valid transitions."""
    validator = MetadataValidator("valid_states", "test")
    errors = validator.validate_transitions()
    assert len(errors) == 0


def test_validate_transitions_invalid(invalid_transition_choices):
    """Test transition validation fails for invalid targets."""
    validator = MetadataValidator("invalid_trans", "test")
    errors = validator.validate_transitions()
    assert len(errors) > 0
    assert errors[0].code == "INVALID_TRANSITION_TARGET"


def test_validate_terminal_states_valid(valid_choices):
    """Test terminal state validation passes."""
    validator = MetadataValidator("valid_states", "test")
    errors = validator.validate_terminal_states()
    assert len(errors) == 0


def test_validate_terminal_states_invalid(terminal_with_transitions):
    """Test terminal state validation fails when terminal has transitions."""
    validator = MetadataValidator("terminal_trans", "test")
    errors = validator.validate_terminal_states()
    assert len(errors) > 0
    assert errors[0].code == "TERMINAL_STATE_HAS_TRANSITIONS"


def test_validate_permission_consistency(valid_choices):
    """Test permission consistency validation."""
    validator = MetadataValidator("valid_states", "test")
    errors = validator.validate_permission_consistency()
    assert len(errors) == 0


def test_validate_no_cycles(valid_choices):
    """Test cycle detection."""
    validator = MetadataValidator("valid_states", "test")
    errors = validator.validate_no_cycles()
    assert len(errors) == 0


def test_validate_no_cycles_with_cycle():
    """Test cycle detection finds cycles."""
    choices = [
        RichChoice(value="a", label="A", metadata={"can_transition_to": ["b"]}),
        RichChoice(value="b", label="B", metadata={"can_transition_to": ["c"]}),
        RichChoice(value="c", label="C", metadata={"can_transition_to": ["a"]}),
    ]
    registry.register("cycle_states", choices, domain="test")

    validator = MetadataValidator("cycle_states", "test")
    errors = validator.validate_no_cycles()
    assert len(errors) > 0
    assert errors[0].code == "STATE_CYCLE_DETECTED"

    registry.clear_domain("test")


def test_validate_reachability(valid_choices):
    """Test reachability validation."""
    validator = MetadataValidator("valid_states", "test")
    errors = validator.validate_reachability()
    # Should pass - approved and rejected are reachable from pending
    assert len(errors) == 0


def test_validate_reachability_unreachable():
    """Test reachability detects unreachable states."""
    choices = [
        RichChoice(
            value="pending",
            label="Pending",
            metadata={"can_transition_to": ["approved"]},
        ),
        RichChoice(value="approved", label="Approved", metadata={"is_final": True}),
        RichChoice(
            value="orphan",
            label="Orphan",
            metadata={"can_transition_to": []},
        ),
    ]
    registry.register("unreachable_states", choices, domain="test")

    validator = MetadataValidator("unreachable_states", "test")
    errors = validator.validate_reachability()
    # Orphan state should be flagged as unreachable
    assert len(errors) > 0

    registry.clear_domain("test")


def test_generate_validation_report(valid_choices):
    """Test validation report generation."""
    validator = MetadataValidator("valid_states", "test")
    report = validator.generate_validation_report()
    assert isinstance(report, str)
    assert "valid_states" in report
    assert "VALID" in report


def test_validate_on_registration_valid(valid_choices):
    """Test validate_on_registration succeeds for valid choices."""
    result = validate_on_registration("valid_states", "test")
    assert result is True


def test_validate_on_registration_invalid(invalid_transition_choices):
    """Test validate_on_registration raises error for invalid choices."""
    with pytest.raises(ValueError) as exc_info:
        validate_on_registration("invalid_trans", "test")
    assert "Validation failed" in str(exc_info.value)
