"""Tests for mutation error array handling.

Validates that mutations can return arrays of error objects in GraphQL
responses, properly serialized and accessible to clients.

Tests cover automatic error generation from status strings, explicit error
arrays via metadata, and backward compatibility with existing patterns.
"""

import pytest


class TestAutoGeneratedErrors:
    """Test automatic error array generation from status strings."""

    @pytest.mark.asyncio
    async def test_auto_generated_errors_from_status(
        self,
        blog_simple_app_with_native_errors,
        blog_simple_graphql_client,
    ):
        """Test that error responses automatically include errors array
        populated from status string, WITHOUT using MutationResultBase.

        This test uses the schema refresh API to dynamically discover test
        mutations created in the fixture.
        """
        # Execute mutation
        query = """
            mutation {
                testAutoError {
                    code
                    status
                    message
                    errors {
                        code
                        identifier
                        message
                        details
                    }
                }
            }
        """
        result = await blog_simple_graphql_client.execute(query)

        # Assertions
        assert result.get("errors") is None, f"GraphQL errors occurred: {result.get('errors')}"
        data = result["data"]["testAutoError"]

        assert data["code"] == 400
        assert data["status"] == "failed:validation"
        assert data["message"] == "Validation failed"

        # ✅ THIS IS THE KEY TEST - errors array should exist and be populated
        assert "errors" in data, "errors field missing from response"
        assert data["errors"] is not None, "errors field is None"
        assert len(data["errors"]) == 1, f"Expected 1 error, got {len(data['errors'])}"
        assert data["errors"][0]["code"] == 400
        assert data["errors"][0]["identifier"] == "validation"
        assert data["errors"][0]["message"] == "Validation failed"
        assert data["errors"][0]["details"] is None

    @pytest.mark.asyncio
    async def test_auto_generated_errors_multiple_status_formats(
        self,
        blog_simple_app_with_native_errors,
        blog_simple_graphql_client,
    ):
        """Test error generation from different status string formats:
        - failed:validation -> identifier="validation"
        - noop:not_found -> identifier="not_found"
        - failed:authorization -> identifier="authorization"
        - failed -> identifier="general_error"
        """
        test_cases = [
            ("failed:validation", "validation", "testStatusValidation"),
            ("noop:not_found", "not_found", "testStatusNotfound"),
            ("failed:authorization", "authorization", "testStatusAuthorization"),
            ("failed", "general_error", "testStatusGeneralerror"),
        ]

        for status, expected_id, mutation_name in test_cases:
            # Execute query (function already exists in schema)
            query = f"""
                mutation {{
                    {mutation_name} {{
                        code
                        errors {{
                            identifier
                        }}
                    }}
                }}
            """
            result = await blog_simple_graphql_client.execute(query)

            assert result.get("errors") is None, (
                f"GraphQL errors for {status}: {result.get('errors')}"
            )
            data = result["data"][mutation_name]

            # Verify error array generated
            assert "errors" in data, f"errors field missing for status '{status}'"
            assert data["errors"] is not None, f"errors is None for status '{status}'"
            assert len(data["errors"]) > 0, f"errors array empty for status '{status}'"
            assert data["errors"][0]["identifier"] == expected_id, (
                f"Expected identifier '{expected_id}' for status '{status}', got '{data['errors'][0]['identifier']}'"
            )


class TestExplicitErrors:
    """Test explicit error arrays via metadata.errors."""

    @pytest.mark.asyncio
    async def test_explicit_errors_override_auto_generation(
        self,
        blog_simple_app_with_native_errors,
        blog_simple_graphql_client,
    ):
        """Test that explicit errors in metadata.errors override auto-generation.
        This supports Pattern 2 (explicit validation errors).
        """
        query = """
            mutation {
                testExplicitErrors {
                    code
                    message
                    errors {
                        code
                        identifier
                        message
                        details
                    }
                }
            }
        """
        result = await blog_simple_graphql_client.execute(query)

        assert result.get("errors") is None, f"GraphQL errors occurred: {result.get('errors')}"
        data = result["data"]["testExplicitErrors"]

        # Should have 2 explicit errors, NOT auto-generated single error
        assert "errors" in data, "errors field missing"
        assert data["errors"] is not None, "errors is None"
        assert len(data["errors"]) == 2, f"Expected 2 explicit errors, got {len(data['errors'])}"

        # Verify first error
        assert data["errors"][0]["identifier"] == "email_invalid"
        assert data["errors"][0]["message"] == "Email format is invalid"
        assert data["errors"][0]["details"]["field"] == "email"

        # Verify second error
        assert data["errors"][1]["identifier"] == "password_weak"
        assert data["errors"][1]["message"] == "Password must be at least 8 characters"
        assert data["errors"][1]["details"]["field"] == "password"


class TestBackwardCompatibility:
    """Test backward compatibility with MutationResultBase."""

    @pytest.mark.asyncio
    async def test_backward_compatibility_with_mutation_result_base(
        self,
        blog_simple_app_with_native_errors,
        blog_simple_graphql_client,
    ):
        """Test that MutationResultBase still works (for backward compatibility).
        This ensures existing code using the base class continues to function.
        """
        query = """
            mutation {
                testWithBase {
                    code
                    status
                    message
                    errors {
                        code
                        identifier
                        message
                    }
                }
            }
        """
        result = await blog_simple_graphql_client.execute(query)

        # Should still work - errors populated
        assert result.get("errors") is None, f"GraphQL errors occurred: {result.get('errors')}"
        data = result["data"]["testWithBase"]

        assert "errors" in data, "errors field missing with MutationResultBase pattern"
        assert data["errors"] is not None, "errors is None"
        assert len(data["errors"]) == 1, f"Expected 1 error, got {len(data['errors'])}"
        assert data["errors"][0]["identifier"] == "validation"
