"""Tests for ZUGFeRD validator using TDD approach."""

import pytest
from src.models import (
    XmlData,
    Supplier,
    Buyer,
    LineItem,
    Totals,
    VatBreakdown,
    PaymentTerms,
    ErrorDetail,
    ValidationResult,
    ValidateRequest,
)
from src.validator import (
    validate_pflichtfelder,
    validate_betraege,
    validate_ustid,
    validate_pdf_abgleich,
    validate_invoice,
)


class TestValidatePflichtfelder:
    """Tests for validate_pflichtfelder function."""

    def test_complete_data_no_errors(self):
        """Complete data should pass validation."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            due_date="2024-02-15",
            supplier=Supplier(
                name="Test Supplier GmbH",
                vat_id="DE123456789",
            ),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=10.0,
                    unit="Stück",
                    unit_price=99.99,
                    line_total=999.90,
                    vat_rate=19.0,
                )
            ],
            totals=Totals(
                line_total_sum=999.90,
                net=999.90,
                vat_total=189.98,
                gross=1189.88,
                vat_breakdown=[VatBreakdown(rate=19.0, base=999.90, amount=189.98)],
            ),
            payment_terms=PaymentTerms(iban="DE89370400440532013000"),
        )
        errors = validate_pflichtfelder(xml_data)
        assert len(errors) == 0

    def test_missing_invoice_number_critical(self):
        """Missing invoice_number should produce critical error."""
        xml_data = XmlData(
            invoice_number="",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "invoice_number" for e in errors)
        assert any(e.severity == "critical" for e in errors)
        assert any(e.error_code == "missing_required" for e in errors)

    def test_missing_invoice_date_critical(self):
        """Missing invoice_date should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "invoice_date" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_supplier_name_critical(self):
        """Missing supplier.name should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "supplier.name" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_supplier_vat_id_critical(self):
        """Missing supplier.vat_id should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id=""),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "supplier.vat_id" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_buyer_name_critical(self):
        """Missing buyer.name should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name=""),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "buyer.name" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_totals_net_critical(self):
        """Missing totals.net should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=0.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "totals.net" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_totals_gross_critical(self):
        """Missing totals.gross should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=0.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "totals.gross" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_totals_vat_total_critical(self):
        """Missing totals.vat_total should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "totals.vat_total" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_due_date_warning(self):
        """Missing due_date should produce warning, not critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            due_date="",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "due_date" for e in errors)
        assert any(e.severity == "warning" for e in errors)

    def test_missing_payment_iban_warning(self):
        """Missing payment_terms.iban should produce warning."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
            payment_terms=PaymentTerms(iban=""),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "payment_terms.iban" for e in errors)
        assert any(e.severity == "warning" for e in errors)

    def test_missing_line_items_critical(self):
        """Empty line_items should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[],
            totals=Totals(line_total_sum=0.0, net=0.0, vat_total=0.0, gross=0.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "line_items" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_line_item_description_critical(self):
        """Missing line_item.description should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "line_items[0].description" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_line_item_quantity_critical(self):
        """Missing line_item.quantity (zero) should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=0.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "line_items[0].quantity" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_line_item_unit_price_critical(self):
        """Missing line_item.unit_price (zero) should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=0.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "line_items[0].unit_price" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_line_item_line_total_critical(self):
        """Missing line_item.line_total (zero) should produce critical error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=0.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "line_items[0].line_total" for e in errors)
        assert any(e.severity == "critical" for e in errors)

    def test_missing_line_item_vat_rate_warning(self):
        """Missing line_item.vat_rate should produce warning."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Test Product",
                    quantity=1.0,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=10.0,
                )
            ],
            totals=Totals(line_total_sum=10.0, net=10.0, vat_total=0.0, gross=10.0),
        )
        errors = validate_pflichtfelder(xml_data)
        assert any(e.field == "line_items[0].vat_rate" for e in errors)
        assert any(e.severity == "warning" for e in errors)

    def test_multiple_critical_errors(self):
        """Multiple missing critical fields should all be reported."""
        xml_data = XmlData(
            invoice_number="",
            invoice_date="",
            supplier=Supplier(name="", vat_id=""),
            buyer=Buyer(name=""),
            line_items=[],
            totals=Totals(line_total_sum=0.0, net=0.0, vat_total=0.0, gross=0.0),
        )
        errors = validate_pflichtfelder(xml_data)
        critical_errors = [e for e in errors if e.severity == "critical"]
        assert len(critical_errors) > 0
        # Should include at least: invoice_number, invoice_date, supplier.name, etc.


class TestValidateBetraege:
    """Tests for validate_betraege function."""

    def test_correct_calculations_no_errors(self):
        """Correct calculations should pass validation."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product A",
                    quantity=10.0,
                    unit="Stück",
                    unit_price=9.99,
                    line_total=99.90,
                    vat_rate=19.0,
                ),
                LineItem(
                    position=2,
                    description="Product B",
                    quantity=5.0,
                    unit="Stück",
                    unit_price=19.99,
                    line_total=99.95,
                    vat_rate=19.0,
                ),
            ],
            totals=Totals(
                line_total_sum=199.85,
                net=199.85,
                vat_total=37.97,
                gross=237.82,
                vat_breakdown=[VatBreakdown(rate=19.0, base=199.85, amount=37.97)],
            ),
        )
        errors = validate_betraege(xml_data)
        assert len(errors) == 0

    def test_line_total_mismatch(self):
        """line_total should equal quantity × unit_price."""
        line = LineItem(
            position=1,
            description="Test Product",
            quantity=10,
            unit="Stück",
            unit_price=9.99,
            line_total=100.00,  # Wrong: 10 × 9.99 = 99.90
        )
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[line],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=0.0, gross=100.0),
        )
        errors = validate_betraege(xml_data)
        assert any(e.error_code == "calculation_mismatch" for e in errors)
        assert any(
            "line_total" in e.field
            for e in errors
            if e.error_code == "calculation_mismatch"
        )

    def test_line_total_within_tolerance(self):
        """line_total should match within tolerance."""
        line = LineItem(
            position=1,
            description="Test Product",
            quantity=10,
            unit="Stück",
            unit_price=9.99,
            line_total=99.91,  # Close enough to 99.90 (within 0.01)
        )
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[line],
            totals=Totals(line_total_sum=99.91, net=99.91, vat_total=0.0, gross=99.91),
        )
        errors = validate_betraege(xml_data)
        # No error should be raised for small differences within tolerance
        line_total_errors = [
            e
            for e in errors
            if "line_total" in str(e.field) and e.error_code == "calculation_mismatch"
        ]
        assert len(line_total_errors) == 0

    def test_totals_net_mismatch(self):
        """totals.net should equal sum of line_items.line_total."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product A",
                    quantity=10,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=100.0,
                ),
                LineItem(
                    position=2,
                    description="Product B",
                    quantity=5,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=50.0,
                ),
            ],
            totals=Totals(
                line_total_sum=150.0,
                net=160.0,  # Wrong: 100 + 50 = 150
                vat_total=0.0,
                gross=160.0,
            ),
        )
        errors = validate_betraege(xml_data)
        assert any(e.field == "totals.net" for e in errors)
        assert any(e.error_code == "calculation_mismatch" for e in errors)

    def test_vat_breakdown_amount_mismatch(self):
        """vat_breakdown.amount should equal base × (rate/100)."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(
                line_total_sum=100.0,
                net=100.0,
                vat_total=20.0,
                gross=120.0,
                vat_breakdown=[
                    VatBreakdown(
                        rate=19.0, base=100.0, amount=25.0
                    )  # Wrong: 100 × 0.19 = 19.0
                ],
            ),
        )
        errors = validate_betraege(xml_data)
        assert any("vat_breakdown" in e.field for e in errors)
        assert any(e.error_code == "calculation_mismatch" for e in errors)

    def test_totals_vat_total_mismatch(self):
        """totals.vat_total should equal sum of vat_breakdown.amount."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(
                line_total_sum=100.0,
                net=100.0,
                vat_total=25.0,  # Wrong: 19.0
                gross=125.0,
                vat_breakdown=[VatBreakdown(rate=19.0, base=100.0, amount=19.0)],
            ),
        )
        errors = validate_betraege(xml_data)
        assert any(e.field == "totals.vat_total" for e in errors)
        assert any(e.error_code == "calculation_mismatch" for e in errors)

    def test_totals_gross_mismatch(self):
        """totals.gross should equal totals.net + totals.vat_total."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(
                line_total_sum=100.0,
                net=100.0,
                vat_total=19.0,
                gross=130.0,  # Wrong: 100 + 19 = 119
                vat_breakdown=[VatBreakdown(rate=19.0, base=100.0, amount=19.0)],
            ),
        )
        errors = validate_betraege(xml_data)
        assert any(e.field == "totals.gross" for e in errors)
        assert any(e.error_code == "calculation_mismatch" for e in errors)

    def test_multiple_calculation_errors(self):
        """Multiple calculation errors should all be reported."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=10,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=150.0,  # Wrong: 10 × 10 = 100
                )
            ],
            totals=Totals(
                line_total_sum=150.0,
                net=200.0,  # Wrong: should be 150
                vat_total=30.0,  # Wrong: 150 × 0.19 = 28.5
                gross=250.0,  # Wrong: 200 + 30 = 230
                vat_breakdown=[
                    VatBreakdown(
                        rate=19.0, base=150.0, amount=50.0
                    )  # Wrong: 150 × 0.19 = 28.5
                ],
            ),
        )
        errors = validate_betraege(xml_data)
        calc_errors = [e for e in errors if e.error_code == "calculation_mismatch"]
        assert len(calc_errors) > 1


class TestValidateUstid:
    """Tests for validate_ustid function."""

    def test_valid_german_vat_id(self):
        """Valid German VAT ID should pass."""
        error = validate_ustid("DE123456789")
        assert error is None

    def test_valid_austrian_vat_id(self):
        """Valid Austrian VAT ID should pass."""
        error = validate_ustid("ATU12345678")
        assert error is None

    def test_valid_swiss_vat_id_mwst(self):
        """Valid Swiss VAT ID with MWST suffix should pass."""
        error = validate_ustid("CHE123456789MWST")
        assert error is None

    def test_valid_swiss_vat_id_tva(self):
        """Valid Swiss VAT ID with TVA suffix should pass."""
        error = validate_ustid("CHE123456789TVA")
        assert error is None

    def test_valid_swiss_vat_id_iva(self):
        """Valid Swiss VAT ID with IVA suffix should pass."""
        error = validate_ustid("CHE123456789IVA")
        assert error is None

    def test_invalid_german_too_short(self):
        """German VAT ID too short should fail."""
        error = validate_ustid("DE12345")
        assert error is not None
        assert error.error_code == "invalid_format"

    def test_invalid_german_too_long(self):
        """German VAT ID too long should fail."""
        error = validate_ustid("DE1234567890")
        assert error is not None
        assert error.error_code == "invalid_format"

    def test_invalid_austrian_wrong_format(self):
        """Austrian VAT ID without U should fail."""
        error = validate_ustid("AT12345678")
        assert error is not None
        assert error.error_code == "invalid_format"

    def test_invalid_swiss_wrong_suffix(self):
        """Swiss VAT ID with wrong suffix should fail."""
        error = validate_ustid("CHE123456789XXX")
        assert error is not None
        assert error.error_code == "invalid_format"

    def test_invalid_country_code(self):
        """Unknown country code should fail."""
        error = validate_ustid("FR123456789")
        assert error is not None
        assert error.error_code == "invalid_format"

    def test_empty_vat_id(self):
        """Empty VAT ID should fail."""
        error = validate_ustid("")
        assert error is not None
        assert error.error_code == "invalid_format"


class TestValidatePdfAbgleich:
    """Tests for validate_pdf_abgleich function."""

    def test_exact_match_invoice_number(self):
        """Exact match for invoice_number should pass."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        pdf_values = {"invoice_number": "INV001", "totals.gross": "119.00"}
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        # No error for exact match
        invoice_errors = [e for e in errors if e.field == "invoice_number"]
        assert len(invoice_errors) == 0

    def test_mismatch_invoice_number(self):
        """Mismatch in invoice_number should produce error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        pdf_values = {"invoice_number": "INV002"}
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        assert any(e.field == "invoice_number" for e in errors)

    def test_match_totals_gross(self):
        """Matching totals.gross within tolerance should pass."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        pdf_values = {"totals.gross": "119.00"}
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        gross_errors = [e for e in errors if e.field == "totals.gross"]
        assert len(gross_errors) == 0

    def test_mismatch_totals_gross(self):
        """Mismatch in totals.gross should produce error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        pdf_values = {"totals.gross": "150.00"}
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        assert any(e.field == "totals.gross" for e in errors)

    def test_match_within_tolerance(self):
        """Values within tolerance should pass."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        # 119.01 is within 0.01 of 119.0
        pdf_values = {"totals.gross": "119.01"}
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        gross_errors = [e for e in errors if e.field == "totals.gross"]
        assert len(gross_errors) == 0

    def test_missing_pdf_value(self):
        """Missing PDF value should not produce error."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        pdf_values = {}
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        # No errors when PDF values are missing (can't compare)
        assert len(errors) == 0

    def test_multiple_mismatches(self):
        """Multiple mismatches should all be reported."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        pdf_values = {
            "invoice_number": "INV002",
            "totals.gross": "150.00",
            "totals.net": "120.00",
            "totals.vat_total": "25.00",
        }
        errors = validate_pdf_abgleich(xml_data, pdf_values)
        # Should have errors for all mismatches
        assert len(errors) >= 3


class TestValidateInvoice:
    """Tests for validate_invoice function."""

    def test_run_single_check(self):
        """Run single validation check."""
        xml_data = XmlData(
            invoice_number="",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["pflichtfelder"],
        )
        result = validate_invoice(request)
        assert result.is_valid is False  # Missing invoice_number
        assert len(result.errors) > 0
        assert result.summary is not None

    def test_run_multiple_checks(self):
        """Run multiple validation checks."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=10,
                    unit="Stück",
                    unit_price=10.0,
                    line_total=150.0,  # Wrong: 10 × 10 = 100
                )
            ],
            totals=Totals(
                line_total_sum=150.0,
                net=200.0,
                vat_total=30.0,
                gross=250.0,
                vat_breakdown=[VatBreakdown(rate=19.0, base=150.0, amount=50.0)],
            ),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["pflichtfelder", "betraege", "ustid"],
        )
        result = validate_invoice(request)
        assert result.is_valid is False
        assert result.summary is not None
        assert result.summary["total_checks"] == 3
        # Should have errors from multiple checks
        total_issues = len(result.errors) + len(result.warnings)
        assert total_issues > 0

    def test_all_checks_pass(self):
        """All checks passing should return is_valid=True."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            due_date="2024-02-15",
            supplier=Supplier(
                name="Test Supplier GmbH",
                vat_id="DE123456789",
            ),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product A",
                    quantity=10.0,
                    unit="Stück",
                    unit_price=9.99,
                    line_total=99.90,
                    vat_rate=19.0,
                ),
            ],
            totals=Totals(
                line_total_sum=99.90,
                net=99.90,
                vat_total=18.98,
                gross=118.88,
                vat_breakdown=[VatBreakdown(rate=19.0, base=99.90, amount=18.98)],
            ),
            payment_terms=PaymentTerms(iban="DE89370400440532013000"),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["pflichtfelder", "betraege"],
        )
        result = validate_invoice(request)
        assert result.is_valid is True
        assert len(result.errors) == 0
        assert result.summary["checks_passed"] == 2

    def test_warnings_not_critical(self):
        """Warnings should not make is_valid=False."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        # Missing due_date and payment_terms.iban are warnings
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["pflichtfelder"],
        )
        result = validate_invoice(request)
        # Should have warnings but be valid
        assert len(result.warnings) > 0
        assert result.is_valid is True  # No critical errors

    def test_summary_counts_correctly(self):
        """Summary should correctly count checks and errors."""
        xml_data = XmlData(
            invoice_number="",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=150.0,
                )
            ],
            totals=Totals(
                line_total_sum=150.0,
                net=200.0,
                vat_total=30.0,
                gross=250.0,
                vat_breakdown=[VatBreakdown(rate=19.0, base=150.0, amount=50.0)],
            ),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["pflichtfelder", "betraege", "ustid"],
        )
        result = validate_invoice(request)
        assert result.summary is not None
        assert result.summary["total_checks"] == 3
        assert "checks_passed" in result.summary
        assert "checks_failed" in result.summary
        assert "critical_errors" in result.summary
        assert "warnings" in result.summary

    def test_with_pdf_abgleich(self):
        """PDF comparison should work with validate_invoice."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            pdf_text="Invoice INV001 Total: 150.00",  # Mismatch
            checks=["pdf_abgleich"],
        )
        result = validate_invoice(request)
        # Should have errors from PDF comparison
        assert result.summary["total_checks"] == 1

    def test_validation_time_populated(self):
        """validation_time_ms should be populated."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["pflichtfelder"],
        )
        result = validate_invoice(request)
        assert result.validation_time_ms >= 0

    def test_invalid_check_name_ignored(self):
        """Invalid check names should be ignored."""
        xml_data = XmlData(
            invoice_number="INV001",
            invoice_date="2024-01-15",
            supplier=Supplier(name="Test Supplier GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Test Buyer AG"),
            line_items=[
                LineItem(
                    position=1,
                    description="Product",
                    quantity=1,
                    unit="Stück",
                    unit_price=100.0,
                    line_total=100.0,
                )
            ],
            totals=Totals(line_total_sum=100.0, net=100.0, vat_total=19.0, gross=119.0),
        )
        request = ValidateRequest(
            xml_data=xml_data.model_dump(),
            checks=["invalid_check"],
        )
        result = validate_invoice(request)
        # Should handle gracefully - no errors from invalid check
        assert result.summary["total_checks"] == 0
        assert result.is_valid is True  # No critical errors
