"""
Tests for InvoiceValidator - TDD RED phase.

Test invoice field validation logic.
"""
import pytest

from backend.domain.invoice_validator import (
    InvoiceValidator,
    ValidationResult,
    ValidationIssue,
)


class TestInvoiceValidator:
    """Test invoice validation logic."""

    @pytest.fixture
    def validator(self) -> InvoiceValidator:
        """Create validator instance with default settings."""
        return InvoiceValidator()

    @pytest.fixture
    def validator_strict(self) -> InvoiceValidator:
        """Create validator with strict confidence threshold."""
        return InvoiceValidator(min_confidence=0.8)

    # ==================== Valid Invoice Tests ====================

    def test_validate_complete_invoice_is_valid(
        self, validator: InvoiceValidator
    ) -> None:
        """Complete invoice with all required fields is valid."""
        fields = {
            "Amount": "1200.00",
            "OCR": "123456789012",
            "Bankgiro": "123-4567",
        }
        confidence = {
            "Amount": 0.95,
            "OCR": 0.90,
            "Bankgiro": 0.85,
        }

        result = validator.validate(fields, confidence)

        assert result.is_valid is True
        assert len([i for i in result.issues if i.severity == "error"]) == 0

    def test_validate_invoice_with_payment_line_is_valid(
        self, validator: InvoiceValidator
    ) -> None:
        """Invoice with payment_line as payment reference is valid."""
        fields = {
            "Amount": "500.00",
            "payment_line": "# 123 # 500 00 5 > 308#",
        }
        confidence = {"Amount": 0.9, "payment_line": 0.85}

        result = validator.validate(fields, confidence)

        assert result.is_valid is True

    # ==================== Invalid Invoice Tests ====================

    def test_validate_missing_amount_is_invalid(
        self, validator: InvoiceValidator
    ) -> None:
        """Missing Amount field should produce error."""
        fields = {
            "OCR": "123456789012",
            "Bankgiro": "123-4567",
        }
        confidence = {"OCR": 0.9, "Bankgiro": 0.85}

        result = validator.validate(fields, confidence)

        assert result.is_valid is False
        error_fields = [i.field for i in result.issues if i.severity == "error"]
        assert "Amount" in error_fields

    def test_validate_missing_payment_reference_produces_warning(
        self, validator: InvoiceValidator
    ) -> None:
        """Missing all payment references should produce warning."""
        fields = {"Amount": "1200.00"}
        confidence = {"Amount": 0.9}

        result = validator.validate(fields, confidence)

        # Missing payment ref is warning, not error
        warning_fields = [i.field for i in result.issues if i.severity == "warning"]
        assert "payment_reference" in warning_fields

    # ==================== Confidence Threshold Tests ====================

    def test_validate_low_confidence_produces_warning(
        self, validator: InvoiceValidator
    ) -> None:
        """Fields below confidence threshold should produce warning."""
        fields = {
            "Amount": "1200.00",
            "OCR": "123456789012",
        }
        confidence = {
            "Amount": 0.9,
            "OCR": 0.3,  # Below default threshold of 0.5
        }

        result = validator.validate(fields, confidence)

        low_conf_warnings = [
            i for i in result.issues
            if i.severity == "warning" and "confidence" in i.message.lower()
        ]
        assert len(low_conf_warnings) > 0

    def test_validate_strict_threshold_more_warnings(
        self, validator_strict: InvoiceValidator
    ) -> None:
        """Strict validator should produce more warnings."""
        fields = {
            "Amount": "1200.00",
            "OCR": "123456789012",
        }
        confidence = {
            "Amount": 0.7,  # Below 0.8 threshold
            "OCR": 0.6,  # Below 0.8 threshold
        }

        result = validator_strict.validate(fields, confidence)

        low_conf_warnings = [
            i for i in result.issues
            if i.severity == "warning" and "confidence" in i.message.lower()
        ]
        assert len(low_conf_warnings) >= 2

    # ==================== Edge Cases ====================

    def test_validate_empty_fields_is_invalid(
        self, validator: InvoiceValidator
    ) -> None:
        """Empty fields dict should be invalid."""
        fields: dict[str, str | None] = {}
        confidence: dict[str, float] = {}

        result = validator.validate(fields, confidence)

        assert result.is_valid is False

    def test_validate_none_field_values_treated_as_missing(
        self, validator: InvoiceValidator
    ) -> None:
        """None values should be treated as missing."""
        fields = {
            "Amount": None,
            "OCR": "123456789012",
        }
        confidence = {"OCR": 0.9}

        result = validator.validate(fields, confidence)

        assert result.is_valid is False
        error_fields = [i.field for i in result.issues if i.severity == "error"]
        assert "Amount" in error_fields

    def test_validate_empty_string_treated_as_missing(
        self, validator: InvoiceValidator
    ) -> None:
        """Empty string should be treated as missing."""
        fields = {
            "Amount": "",
            "OCR": "123456789012",
        }
        confidence = {"OCR": 0.9}

        result = validator.validate(fields, confidence)

        assert result.is_valid is False

    # ==================== ValidationResult Properties ====================

    def test_validation_result_is_immutable(
        self, validator: InvoiceValidator
    ) -> None:
        """ValidationResult should be a frozen dataclass."""
        fields = {"Amount": "100.00", "OCR": "123"}
        confidence = {"Amount": 0.9, "OCR": 0.9}
        result = validator.validate(fields, confidence)

        with pytest.raises((AttributeError, TypeError)):
            result.is_valid = False  # type: ignore

    def test_validation_result_issues_is_tuple(
        self, validator: InvoiceValidator
    ) -> None:
        """Issues should be a tuple (immutable)."""
        fields = {"Amount": "100.00"}
        confidence = {"Amount": 0.9}
        result = validator.validate(fields, confidence)

        assert isinstance(result.issues, tuple)

    def test_validation_result_has_confidence(
        self, validator: InvoiceValidator
    ) -> None:
        """ValidationResult should have confidence score."""
        fields = {"Amount": "100.00", "OCR": "123"}
        confidence = {"Amount": 0.9, "OCR": 0.8}
        result = validator.validate(fields, confidence)

        assert hasattr(result, "confidence")
        assert 0.0 <= result.confidence <= 1.0

    # ==================== ValidationIssue Tests ====================

    def test_validation_issue_has_required_fields(
        self, validator: InvoiceValidator
    ) -> None:
        """ValidationIssue must have field, severity, message."""
        fields: dict[str, str | None] = {}
        confidence: dict[str, float] = {}
        result = validator.validate(fields, confidence)

        assert len(result.issues) > 0
        issue = result.issues[0]

        assert hasattr(issue, "field")
        assert hasattr(issue, "severity")
        assert hasattr(issue, "message")
        assert issue.severity in ("error", "warning", "info")
