"""Tests for validation utilities."""

from datetime import UTC, datetime
from uuid import uuid4

import pytest
from pydantic import ValidationError

from ingress_recipient.validation import safe_validate_recipient_data, validate_recipient_data


class TestValidateRecipientData:
    """Tests for validate_recipient_data function."""

    def test_valid_data_returns_recipient_data(self, valid_recipient_data_dict):
        """Test that valid data returns RecipientData object."""
        result = validate_recipient_data(valid_recipient_data_dict)

        assert result.channel_id == "ch_maritime_telemetry"
        assert isinstance(result.tenant_id, type(uuid4()))
        assert result.data["vessel_id"] == "v_12345"

    def test_invalid_data_raises_validation_error(self):
        """Test that invalid data raises ValidationError."""
        invalid_data = {
            "channel_id": "",  # Invalid - empty
            "tenant_id": str(uuid4()),
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        with pytest.raises(ValidationError):
            validate_recipient_data(invalid_data)

    def test_missing_required_fields_raises_error(self):
        """Test that missing required fields raises ValidationError."""
        invalid_data = {"channel_id": "ch_test"}

        with pytest.raises(ValidationError):
            validate_recipient_data(invalid_data)

    def test_invalid_uuid_raises_error(self):
        """Test that invalid UUID raises ValidationError."""
        invalid_data = {
            "channel_id": "ch_test",
            "tenant_id": "not-a-uuid",
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        with pytest.raises(ValidationError) as exc_info:
            validate_recipient_data(invalid_data)

        errors = exc_info.value.errors()
        assert any("tenant_id" in str(err["loc"]) for err in errors)

    def test_invalid_timestamp_raises_error(self):
        """Test that invalid timestamp raises ValidationError."""
        invalid_data = {
            "channel_id": "ch_test",
            "tenant_id": str(uuid4()),
            "timestamp": "not-a-timestamp",
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        with pytest.raises(ValidationError):
            validate_recipient_data(invalid_data)

    def test_empty_data_object_allowed(self):
        """Test that empty data object is allowed."""
        valid_data = {
            "channel_id": "ch_test",
            "tenant_id": str(uuid4()),
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        result = validate_recipient_data(valid_data)
        assert result.data == {}

    def test_nested_data_preserved(self):
        """Test that nested data structures are preserved."""
        valid_data = {
            "channel_id": "ch_test",
            "tenant_id": str(uuid4()),
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {
                "vessel": {"id": "v_123", "name": "Test Vessel"},
                "position": {"lat": 59.9139, "lng": 10.7522},
            },
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        result = validate_recipient_data(valid_data)
        assert result.data["vessel"]["id"] == "v_123"
        assert result.data["position"]["lat"] == 59.9139


class TestSafeValidateRecipientData:
    """Tests for safe_validate_recipient_data function."""

    def test_valid_data_returns_tuple_with_data(self, valid_recipient_data_dict):
        """Test that valid data returns (RecipientData, None)."""
        validated, errors = safe_validate_recipient_data(valid_recipient_data_dict)

        assert validated is not None
        assert errors is None
        assert validated.channel_id == "ch_maritime_telemetry"

    def test_invalid_data_returns_tuple_with_errors(self):
        """Test that invalid data returns (None, errors)."""
        invalid_data = {
            "channel_id": "",
            "tenant_id": str(uuid4()),
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        validated, errors = safe_validate_recipient_data(invalid_data)

        assert validated is None
        assert errors is not None
        assert isinstance(errors, list)
        assert len(errors) > 0

    def test_error_format(self):
        """Test that errors have correct format."""
        invalid_data = {
            "channel_id": "",
            "tenant_id": "not-a-uuid",
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        validated, errors = safe_validate_recipient_data(invalid_data)

        assert validated is None
        assert errors is not None

        for error in errors:
            assert "loc" in error
            assert "msg" in error
            assert "type" in error
            assert isinstance(error["loc"], list)

    def test_multiple_validation_errors(self):
        """Test that multiple validation errors are returned."""
        invalid_data = {
            "channel_id": "",  # Invalid
            "tenant_id": "not-a-uuid",  # Invalid
            "timestamp": "not-a-timestamp",  # Invalid
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        validated, errors = safe_validate_recipient_data(invalid_data)

        assert validated is None
        assert errors is not None
        assert len(errors) >= 3  # At least 3 errors

    def test_missing_required_fields_returns_errors(self):
        """Test that missing required fields returns errors."""
        invalid_data = {"channel_id": "ch_test"}

        validated, errors = safe_validate_recipient_data(invalid_data)

        assert validated is None
        assert errors is not None
        assert len(errors) > 0

    def test_nested_field_error_location(self):
        """Test that nested field errors have correct location."""
        invalid_data = {
            "channel_id": "ch_test",
            "tenant_id": str(uuid4()),
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {
                "ingress_timestamp": "invalid-timestamp",
            },
        }

        validated, errors = safe_validate_recipient_data(invalid_data)

        assert validated is None
        assert errors is not None

        # Find metadata error
        metadata_errors = [e for e in errors if "metadata" in str(e["loc"])]
        assert len(metadata_errors) > 0

    def test_empty_data_object_succeeds(self):
        """Test that empty data object succeeds."""
        valid_data = {
            "channel_id": "ch_test",
            "tenant_id": str(uuid4()),
            "timestamp": datetime.now(UTC).isoformat(),
            "data": {},
            "metadata": {"ingress_timestamp": datetime.now(UTC).isoformat()},
        }

        validated, errors = safe_validate_recipient_data(valid_data)

        assert validated is not None
        assert errors is None
        assert validated.data == {}

    def test_does_not_raise_exception(self):
        """Test that function never raises exceptions."""
        # Even with completely invalid data, should not raise
        invalid_data = None

        try:
            validated, errors = safe_validate_recipient_data(invalid_data)  # type: ignore
            # Should return errors, not raise
            assert validated is None or errors is not None
        except Exception as e:
            pytest.fail(f"safe_validate_recipient_data should not raise: {e}")
