"""Tests for Pydantic models."""

import pytest

# These tests will fail initially, then pass after models are implemented


class TestExtractionMeta:
    """Test ExtractionMeta model."""

    def test_minimal_extraction_meta(self):
        """Test ExtractionMeta with minimal required fields."""
        from src.models import ExtractionMeta

        meta = ExtractionMeta(pages=1, extraction_time_ms=234)
        assert meta.pages == 1
        assert meta.extraction_time_ms == 234
        assert meta.xml_attachment_name is None

    def test_full_extraction_meta(self):
        """Test ExtractionMeta with all fields."""
        from src.models import ExtractionMeta

        meta = ExtractionMeta(
            pages=2, xml_attachment_name="factur-x.xml", extraction_time_ms=456
        )
        assert meta.pages == 2
        assert meta.xml_attachment_name == "factur-x.xml"
        assert meta.extraction_time_ms == 456


class TestSupplier:
    """Test Supplier model."""

    def test_minimal_supplier(self):
        """Test Supplier with minimal required fields."""
        from src.models import Supplier

        supplier = Supplier(name="ACME GmbH")
        assert supplier.name == "ACME GmbH"
        assert supplier.street is None
        assert supplier.vat_id is None

    def test_full_supplier(self):
        """Test Supplier with all fields."""
        from src.models import Supplier

        supplier = Supplier(
            name="ACME GmbH",
            street="Musterstraße 42",
            postal_code="12345",
            city="Musterstadt",
            country="DE",
            vat_id="DE123456789",
            email="info@acme.de",
        )
        assert supplier.name == "ACME GmbH"
        assert supplier.street == "Musterstraße 42"
        assert supplier.postal_code == "12345"
        assert supplier.city == "Musterstadt"
        assert supplier.country == "DE"
        assert supplier.vat_id == "DE123456789"
        assert supplier.email == "info@acme.de"


class TestBuyer:
    """Test Buyer model."""

    def test_minimal_buyer(self):
        """Test Buyer with minimal required fields."""
        from src.models import Buyer

        buyer = Buyer(name="Customer AG")
        assert buyer.name == "Customer AG"
        assert buyer.street is None

    def test_full_buyer(self):
        """Test Buyer with all fields."""
        from src.models import Buyer

        buyer = Buyer(
            name="Customer AG",
            street="Kundenweg 7",
            postal_code="54321",
            city="Kundenstadt",
            country="DE",
            vat_id="DE987654321",
        )
        assert buyer.name == "Customer AG"
        assert buyer.street == "Kundenweg 7"
        assert buyer.vat_id == "DE987654321"


class TestVatBreakdown:
    """Test VatBreakdown model."""

    def test_vat_breakdown(self):
        """Test VatBreakdown model."""
        from src.models import VatBreakdown

        vat = VatBreakdown(rate=19.0, base=99.90, amount=18.98)
        assert vat.rate == 19.0
        assert vat.base == 99.90
        assert vat.amount == 18.98


class TestPaymentTerms:
    """Test PaymentTerms model."""

    def test_minimal_payment_terms(self):
        """Test PaymentTerms with minimal fields."""
        from src.models import PaymentTerms

        terms = PaymentTerms()
        assert terms.iban is None
        assert terms.bic is None
        assert terms.account_holder is None

    def test_full_payment_terms(self):
        """Test PaymentTerms with all fields."""
        from src.models import PaymentTerms

        terms = PaymentTerms(
            iban="DE89370400440532013000", bic="DEUTDEFF", account_holder="ACME GmbH"
        )
        assert terms.iban == "DE89370400440532013000"
        assert terms.bic == "DEUTDEFF"
        assert terms.account_holder == "ACME GmbH"


class TestTotals:
    """Test Totals model."""

    def test_totals_minimal(self):
        """Test Totals with required fields only."""
        from src.models import Totals

        totals = Totals(line_total_sum=99.90, net=99.90, vat_total=18.98, gross=118.88)
        assert totals.line_total_sum == 99.90
        assert totals.net == 99.90
        assert totals.vat_total == 18.98
        assert totals.gross == 118.88
        assert totals.vat_breakdown == []

    def test_totals_with_vat_breakdown(self):
        """Test Totals with VAT breakdown."""
        from src.models import Totals, VatBreakdown

        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)],
        )
        assert len(totals.vat_breakdown) == 1
        assert totals.vat_breakdown[0].rate == 19.0


class TestLineItem:
    """Test LineItem model."""

    def test_minimal_line_item(self):
        """Test LineItem with minimal required fields."""
        from src.models import LineItem

        item = LineItem(
            position=1,
            description="Widget",
            quantity=10.0,
            unit="Stück",
            unit_price=9.99,
            line_total=99.90,
        )
        assert item.position == 1
        assert item.description == "Widget"
        assert item.quantity == 10.0
        assert item.unit == "Stück"
        assert item.unit_price == 9.99
        assert item.line_total == 99.90
        assert item.article_number is None
        assert item.vat_rate is None

    def test_full_line_item(self):
        """Test LineItem with all fields."""
        from src.models import LineItem

        item = LineItem(
            position=1,
            article_number="ART-001",
            article_number_buyer="KUN-001",
            description="Premium Widget",
            quantity=5.0,
            unit="Stück",
            unit_price=19.99,
            line_total=99.95,
            vat_rate=19.0,
            vat_amount=18.99,
        )
        assert item.article_number == "ART-001"
        assert item.article_number_buyer == "KUN-001"
        assert item.vat_rate == 19.0
        assert item.vat_amount == 18.99


class TestXmlData:
    """Test XmlData model."""

    def test_minimal_xml_data(self):
        """Test XmlData with minimal required fields."""
        from src.models import XmlData, Supplier, Buyer, Totals

        data = XmlData(
            invoice_number="RE-2025-001234",
            invoice_date="2025-02-04",
            supplier={"name": "ACME GmbH"},
            buyer={"name": "Customer AG"},
            line_items=[],
            totals={"line_total_sum": 0.0, "net": 0.0, "vat_total": 0.0, "gross": 0.0},
        )
        assert data.invoice_number == "RE-2025-001234"
        assert data.invoice_date == "2025-02-04"
        assert data.due_date is None
        assert data.notes is None

    def test_full_xml_data(self):
        """Test XmlData with all fields."""
        from src.models import XmlData, Supplier, Buyer, LineItem, Totals, VatBreakdown

        data = XmlData(
            invoice_number="RE-2025-001234",
            invoice_date="2025-02-04",
            due_date="2025-03-04",
            supplier=Supplier(name="ACME GmbH", vat_id="DE123456789"),
            buyer=Buyer(name="Customer AG", vat_id="DE987654321"),
            line_items=[
                LineItem(
                    position=1,
                    description="Widget",
                    quantity=10.0,
                    unit="Stück",
                    unit_price=9.99,
                    line_total=99.90,
                )
            ],
            totals=Totals(
                line_total_sum=99.90,
                net=99.90,
                vat_breakdown=[VatBreakdown(rate=19.0, base=99.90, amount=18.98)],
                vat_total=18.98,
                gross=118.88,
            ),
            currency="EUR",
            notes="Payment due within 30 days",
        )
        assert data.invoice_number == "RE-2025-001234"
        assert data.due_date == "2025-03-04"
        assert data.currency == "EUR"
        assert data.notes == "Payment due within 30 days"
        assert len(data.line_items) == 1


class TestExtractResponse:
    """Test ExtractResponse model."""

    def test_extract_response_zugferd(self):
        """Test ExtractResponse with ZUGFeRD data."""
        from src.models import (
            ExtractResponse,
            XmlData,
            ExtractionMeta,
            Supplier,
            Buyer,
            LineItem,
            Totals,
            VatBreakdown,
        )

        response = ExtractResponse(
            is_zugferd=True,
            zugferd_profil="EN16931",
            xml_raw="<?xml version='1.0'?>...",
            xml_data=XmlData(
                invoice_number="RE-2025-001234",
                invoice_date="2025-02-04",
                supplier=Supplier(name="ACME GmbH"),
                buyer=Buyer(name="Customer AG"),
                line_items=[
                    LineItem(
                        position=1,
                        description="Widget",
                        quantity=10.0,
                        unit="Stück",
                        unit_price=9.99,
                        line_total=99.90,
                    )
                ],
                totals=Totals(
                    line_total_sum=99.90,
                    net=99.90,
                    vat_breakdown=[VatBreakdown(rate=19.0, base=99.90, amount=18.98)],
                    vat_total=18.98,
                    gross=118.88,
                ),
            ),
            pdf_text="Rechnung\n...",
            extraction_meta=ExtractionMeta(
                pages=1, xml_attachment_name="factur-x.xml", extraction_time_ms=234
            ),
        )
        assert response.is_zugferd is True
        assert response.zugferd_profil == "EN16931"
        assert response.xml_raw is not None
        assert response.xml_data is not None
        assert response.pdf_text is not None
        assert response.extraction_meta.pages == 1

    def test_extract_response_non_zugferd(self):
        """Test ExtractResponse for non-ZUGFeRD PDF."""
        from src.models import ExtractResponse, ExtractionMeta

        response = ExtractResponse(
            is_zugferd=False,
            pdf_text="Invoice text from PDF...",
            extraction_meta=ExtractionMeta(pages=1, extraction_time_ms=50),
        )
        assert response.is_zugferd is False
        assert response.zugferd_profil is None
        assert response.xml_raw is None
        assert response.xml_data is None
        assert response.pdf_text is not None


class TestErrorDetail:
    """Test ErrorDetail model."""

    def test_error_detail_critical(self):
        """Test ErrorDetail with critical severity."""
        from src.models import ErrorDetail

        error = ErrorDetail(
            check="pflichtfelder",
            field="invoice_number",
            error_code="missing_required_field",
            message="Invoice number is required",
            severity="critical",
        )
        assert error.check == "pflichtfelder"
        assert error.field == "invoice_number"
        assert error.error_code == "missing_required_field"
        assert error.message == "Invoice number is required"
        assert error.severity == "critical"

    def test_error_detail_warning(self):
        """Test ErrorDetail with warning severity."""
        from src.models import ErrorDetail

        error = ErrorDetail(
            check="pdf_abgleich",
            field=None,
            error_code="value_mismatch",
            message="Amounts differ slightly",
            severity="warning",
        )
        assert error.check == "pdf_abgleich"
        assert error.field is None
        assert error.severity == "warning"


class TestValidationResult:
    """Test ValidationResult model."""

    def test_valid_result(self):
        """Test ValidationResult with no errors."""
        from src.models import ValidationResult

        result = ValidationResult(
            is_valid=True, errors=[], warnings=[], summary=None, validation_time_ms=100
        )
        assert result.is_valid is True
        assert result.errors == []
        assert result.warnings == []
        assert result.summary is None
        assert result.validation_time_ms == 100

    def test_invalid_result_with_errors(self):
        """Test ValidationResult with errors."""
        from src.models import ValidationResult, ErrorDetail

        result = ValidationResult(
            is_valid=False,
            errors=[
                ErrorDetail(
                    check="pflichtfelder",
                    field="invoice_number",
                    error_code="missing_required_field",
                    message="Invoice number is required",
                    severity="critical",
                ),
                ErrorDetail(
                    check="betraege",
                    field="totals.gross",
                    error_code="calculation_mismatch",
                    message="Gross total mismatch: expected 118.88, got 118.90",
                    severity="critical",
                ),
            ],
            warnings=[
                ErrorDetail(
                    check="pflichtfelder",
                    field="due_date",
                    error_code="missing_optional_field",
                    message="Due date not provided",
                    severity="warning",
                )
            ],
            summary={"total_errors": 2, "total_warnings": 1, "critical_errors": 2},
            validation_time_ms=150,
        )
        assert result.is_valid is False
        assert len(result.errors) == 2
        assert len(result.warnings) == 1
        assert result.summary["total_errors"] == 2
        assert result.validation_time_ms == 150


class TestExtractRequest:
    """Test ExtractRequest model."""

    def test_extract_request(self):
        """Test ExtractRequest model."""
        from src.models import ExtractRequest

        request = ExtractRequest(pdf_base64="JVBERi0xLjQK...")
        assert request.pdf_base64 == "JVBERi0xLjQK..."


class TestValidateRequest:
    """Test ValidateRequest model."""

    def test_validate_request_minimal(self):
        """Test ValidateRequest with minimal fields."""
        from src.models import ValidateRequest

        request = ValidateRequest(xml_data={}, checks=["pflichtfelder"])
        assert request.xml_data == {}
        assert request.checks == ["pflichtfelder"]
        assert request.pdf_text is None

    def test_validate_request_full(self):
        """Test ValidateRequest with all fields."""
        from src.models import ValidateRequest

        request = ValidateRequest(
            xml_data={"invoice_number": "RE-001", "totals": {"gross": 118.88}},
            pdf_text="Invoice text...",
            checks=["pflichtfelder", "betraege", "ustid", "pdf_abgleich"],
        )
        assert request.xml_data["invoice_number"] == "RE-001"
        assert request.pdf_text is not None
        assert len(request.checks) == 4


class TestErrorResponse:
    """Test ErrorResponse model."""

    def test_error_response(self):
        """Test ErrorResponse model."""
        from src.models import ErrorResponse

        response = ErrorResponse(
            error="invalid_pdf", message="The provided file is not a valid PDF"
        )
        assert response.error == "invalid_pdf"
        assert response.message == "The provided file is not a valid PDF"


class TestModelsSerializeToJSON:
    """Test JSON serialization of all models."""

    def test_extract_response_serializes(self):
        """Test ExtractResponse serializes to valid JSON."""
        from src.models import ExtractResponse, ExtractionMeta

        response = ExtractResponse(
            is_zugferd=False,
            pdf_text="Invoice text...",
            extraction_meta=ExtractionMeta(pages=1, extraction_time_ms=50),
        )
        json_str = response.model_dump_json()
        assert '"is_zugferd":false' in json_str
        assert '"pdf_text"' in json_str

    def test_validation_result_serializes(self):
        """Test ValidationResult serializes to valid JSON."""
        from src.models import ValidationResult

        result = ValidationResult(
            is_valid=True, errors=[], warnings=[], summary=None, validation_time_ms=100
        )
        json_str = result.model_dump_json()
        assert '"is_valid":true' in json_str

    def test_models_roundtrip(self):
        """Test models survive JSON roundtrip."""
        from src.models import Supplier

        supplier = Supplier(name="ACME GmbH", vat_id="DE123456789")
        json_str = supplier.model_dump_json()
        supplier2 = Supplier.model_validate_json(json_str)
        assert supplier2.name == supplier.name
        assert supplier2.vat_id == supplier.vat_id
