"""Tests for Pydantic models."""

from __future__ import annotations

import pytest
from pydantic import ValidationError

from odins_eye.models import (
    CreditResponse,
    EsResponse,
    FaceSearchRequest,
    QueryRequest,
    QueryResponse,
    StatusMessage,
    UserInfo,
    UserProfileResponse,
    VersionResponse,
)


class TestStatusMessage:
    """Tests for StatusMessage model."""

    def test_valid_status_message(self):
        """Test creating a valid status message."""
        data = {"status": 200, "message": "OK"}
        model = StatusMessage.model_validate(data)
        assert model.status == 200
        assert model.message == "OK"

    def test_optional_fields(self):
        """Test that status and message are optional."""
        data = {}
        model = StatusMessage.model_validate(data)
        assert model.status is None
        assert model.message is None

    def test_extra_fields_allowed(self):
        """Test that extra fields are allowed."""
        data = {"status": 200, "message": "OK", "extra": "field"}
        model = StatusMessage.model_validate(data)
        assert model.model_dump()["extra"] == "field"


class TestEsResponse:
    """Tests for EsResponse model."""

    def test_valid_es_response(self):
        """Test creating a valid Elasticsearch response."""
        data = {"statusCode": 200, "body": {"hits": {"total": 0}}}
        model = EsResponse.model_validate(data)
        assert model.statusCode == 200
        assert model.body["hits"]["total"] == 0

    def test_missing_required_field(self):
        """Test that missing required fields raise validation error."""
        data = {"body": {}}
        with pytest.raises(ValidationError):
            EsResponse.model_validate(data)


class TestQueryResponse:
    """Tests for QueryResponse model."""

    def test_valid_query_response(self):
        """Test creating a valid query response."""
        data = {"status": 200, "resp": {"statusCode": 200, "body": {}}}
        model = QueryResponse.model_validate(data)
        assert model.status == 200
        assert model.resp.statusCode == 200


class TestCreditResponse:
    """Tests for CreditResponse model."""

    def test_valid_credit_response(self):
        """Test creating a valid credit response."""
        data = {"status": 200, "message": "Credits retrieved", "current_balance": 1000}
        model = CreditResponse.model_validate(data)
        assert model.status == 200
        assert model.current_balance == 1000


class TestUserInfo:
    """Tests for UserInfo model."""

    def test_valid_user_info(self):
        """Test creating valid user info."""
        data = {"name": "John Doe", "age": 30}
        model = UserInfo.model_validate(data)
        assert model.name == "John Doe"
        assert model.age == 30

    def test_missing_required_fields(self):
        """Test that missing required fields raise validation error."""
        data = {"name": "John Doe"}
        with pytest.raises(ValidationError):
            UserInfo.model_validate(data)


class TestUserProfileResponse:
    """Tests for UserProfileResponse model."""

    def test_valid_profile_response(self):
        """Test creating a valid profile response."""
        data = {
            "status": 200,
            "message": "Profile retrieved",
            "user": {"name": "John Doe", "age": 30},
        }
        model = UserProfileResponse.model_validate(data)
        assert model.status == 200
        assert model.user.name == "John Doe"


class TestVersionResponse:
    """Tests for VersionResponse model."""

    def test_valid_version_response(self):
        """Test creating a valid version response."""
        data = {"status": 200, "version": "1.0.0"}
        model = VersionResponse.model_validate(data)
        assert model.status == 200
        assert model.version == "1.0.0"


class TestQueryRequest:
    """Tests for QueryRequest model."""

    def test_valid_query_request(self):
        """Test creating a valid query request."""
        data = {"query": {"match_all": {}}}
        model = QueryRequest.model_validate(data)
        assert model.root["query"]["match_all"] == {}

    def test_missing_query_key_raises_validation_error(self):
        """Test that query field is required."""
        with pytest.raises(ValidationError, match="query field is required"):
            QueryRequest.model_validate({})


class TestFaceSearchRequest:
    """Tests for FaceSearchRequest model."""

    def test_valid_face_search_request(self):
        """Test creating a valid face search request."""
        data = {"image_b64": "ZmFrZQ==AAAAAAAAAAAAAAAAAAAA", "top_k": 3}
        model = FaceSearchRequest.model_validate(data)
        assert model.image_b64 == "ZmFrZQ==AAAAAAAAAAAAAAAAAAAA"
        assert model.top_k == 3

    def test_legacy_face_search_aliases_are_accepted(self):
        """Test that legacy aliases are accepted."""
        model = FaceSearchRequest.model_validate(
            {"image_base64": "ZmFrZQ==AAAAAAAAAAAAAAAAAAAA", "max_k": 7}
        )
        assert model.image_b64 == "ZmFrZQ==AAAAAAAAAAAAAAAAAAAA"
        assert model.top_k == 7

    def test_embedding_face_search_request_is_valid(self):
        """Test embedding-only face search request."""
        model = FaceSearchRequest.model_validate({"embedding": [0.5] * 512, "top_k": 2})
        assert model.embedding == [0.5] * 512
        assert model.top_k == 2

    def test_missing_face_search_input_raises_validation_error(self):
        """Test that image_b64 or embedding is required."""
        with pytest.raises(ValidationError, match="face search requires image_b64 or embedding"):
            FaceSearchRequest.model_validate({})

    def test_invalid_embedding_length_raises_validation_error(self):
        """Test embedding length validation."""
        with pytest.raises(ValidationError, match="embedding must contain exactly 512 dimensions"):
            FaceSearchRequest.model_validate({"embedding": [0.5] * 511})
