"""Tests for extraction domain models."""

from __future__ import annotations

from extraction.models import (
    ExtractionConfig,
    ExtractionField,
    ExtractionResult,
    ExtractionSchema,
    FieldType,
)


class TestFieldType:
    def test_all_values(self):
        assert FieldType.TEXT == "text"
        assert FieldType.NUMBER == "number"
        assert FieldType.DATE == "date"
        assert FieldType.BOOLEAN == "boolean"
        assert FieldType.CURRENCY == "currency"
        assert FieldType.ENTITY == "entity"
        assert FieldType.LIST == "list"

    def test_is_str_enum(self):
        assert isinstance(FieldType.TEXT, str)
        assert f"type={FieldType.TEXT}" == "type=text"


class TestExtractionField:
    def test_minimal(self):
        field = ExtractionField(name="title", field_type=FieldType.TEXT)
        assert field.name == "title"
        assert field.field_type == FieldType.TEXT
        assert field.required is True
        assert field.description == ""
        assert field.examples == []

    def test_full(self):
        field = ExtractionField(
            name="amount",
            field_type=FieldType.CURRENCY,
            description="Total monetary amount",
            required=False,
            examples=["USD 100.00", "EUR 50.00"],
        )
        assert field.field_type == FieldType.CURRENCY
        assert field.required is False
        assert len(field.examples) == 2


class TestExtractionSchema:
    def _sample_schema(self) -> ExtractionSchema:
        return ExtractionSchema(
            name="invoice",
            description="Extract invoice fields",
            fields=[
                ExtractionField(name="vendor", field_type=FieldType.TEXT, required=True),
                ExtractionField(
                    name="amount",
                    field_type=FieldType.CURRENCY,
                    description="Total amount",
                    examples=["USD 500.00"],
                ),
                ExtractionField(
                    name="date",
                    field_type=FieldType.DATE,
                    required=False,
                ),
                ExtractionField(
                    name="items",
                    field_type=FieldType.LIST,
                    required=False,
                ),
                ExtractionField(
                    name="paid",
                    field_type=FieldType.BOOLEAN,
                    required=False,
                ),
                ExtractionField(
                    name="quantity",
                    field_type=FieldType.NUMBER,
                    required=False,
                ),
                ExtractionField(
                    name="supplier",
                    field_type=FieldType.ENTITY,
                    required=False,
                ),
            ],
        )

    def test_to_json_schema_structure(self):
        schema = self._sample_schema()
        js = schema.to_json_schema()
        assert js["type"] == "object"
        assert "properties" in js
        assert "vendor" in js["properties"]
        assert "amount" in js["properties"]
        assert "date" in js["properties"]
        assert js["description"] == "Extract invoice fields"

    def test_to_json_schema_required(self):
        schema = self._sample_schema()
        js = schema.to_json_schema()
        assert "vendor" in js["required"]
        assert "amount" in js["required"]
        # date is not required
        assert "date" not in js["required"]

    def test_to_json_schema_field_types(self):
        schema = self._sample_schema()
        js = schema.to_json_schema()
        assert js["properties"]["vendor"]["type"] == "string"
        assert js["properties"]["date"]["type"] == "string"
        assert js["properties"]["date"]["format"] == "date"
        assert js["properties"]["items"]["type"] == "array"
        assert js["properties"]["paid"]["type"] == "boolean"
        assert js["properties"]["quantity"]["type"] == "number"

    def test_to_json_schema_examples(self):
        schema = self._sample_schema()
        js = schema.to_json_schema()
        assert js["properties"]["amount"]["examples"] == ["USD 500.00"]

    def test_to_json_schema_no_description(self):
        schema = ExtractionSchema(
            name="simple",
            fields=[ExtractionField(name="x", field_type=FieldType.TEXT)],
        )
        js = schema.to_json_schema()
        assert "description" not in js

    def test_to_json_schema_no_required(self):
        schema = ExtractionSchema(
            name="optional_only",
            fields=[
                ExtractionField(name="x", field_type=FieldType.TEXT, required=False),
            ],
        )
        js = schema.to_json_schema()
        assert "required" not in js


class TestExtractionResult:
    def test_defaults(self):
        result = ExtractionResult(schema_name="test", data={"key": "value"})
        assert result.confidence == 1.0
        assert result.model_used == ""
        assert result.tokens_used == 0
        assert result.cost_usd == 0.0
        assert result.processing_time_ms == 0
        assert result.warnings == []

    def test_full(self):
        result = ExtractionResult(
            schema_name="invoice",
            data={"vendor": "Acme"},
            confidence=0.95,
            model_used="claude-haiku-3-20250414",
            tokens_used=150,
            cost_usd=0.0001,
            processing_time_ms=200,
            warnings=["Missing field 'date'"],
        )
        assert result.confidence == 0.95
        assert len(result.warnings) == 1

    def test_confidence_bounds(self):
        import pytest

        with pytest.raises(Exception):
            ExtractionResult(schema_name="x", data={}, confidence=1.5)
        with pytest.raises(Exception):
            ExtractionResult(schema_name="x", data={}, confidence=-0.1)


class TestExtractionConfig:
    def test_defaults(self):
        config = ExtractionConfig()
        assert config.default_model == "claude-haiku-3-20250414"
        assert config.temperature == 0.1
        assert config.max_tokens == 4096
        assert config.max_retries == 2

    def test_custom(self):
        config = ExtractionConfig(
            default_model="gpt-4o-mini",
            temperature=0.3,
            max_tokens=2048,
            max_retries=5,
        )
        assert config.default_model == "gpt-4o-mini"
        assert config.max_retries == 5
