"""Basic CRUD tests for Response API.

Tests for Response and Conversation CRUD operations against cloud backends.

Source: Migrated from e2e_response_api/features/test_basic_crud.py
"""

from __future__ import annotations

import logging
import time

import openai
import pytest
from openai import OpenAI
from openai.types import responses

logger = logging.getLogger(__name__)


def wait_for_background_task(
    client: OpenAI, response_id: str, timeout: int = 30, poll_interval: float = 0.5
) -> responses.Response:
    """Wait for background task to complete.

    Args:
        client: OpenAI client
        response_id: Response ID to poll
        timeout: Max seconds to wait
        poll_interval: Seconds between polls

    Returns:
        Final response data

    Raises:
        TimeoutError: If task doesn't complete in time
        AssertionError: If task fails
    """
    start_time = time.time()

    while time.time() - start_time < timeout:
        resp = client.responses.retrieve(response_id=response_id)
        assert resp.error is None
        assert resp.id == response_id

        if resp.status == "completed":
            return resp
        elif resp.status == "failed":
            raise AssertionError(f"Background task failed: {resp.error}")
        elif resp.status == "cancelled":
            raise AssertionError("Background task was cancelled")

        time.sleep(poll_interval)

    raise TimeoutError(
        f"Background task {response_id} did not complete within {timeout}s"
    )


# =============================================================================
# Response CRUD Tests (Memory Storage)
# =============================================================================


@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
class TestResponseCRUD:
    """Tests for Response API CRUD operations."""

    def test_create_and_get_response(self, setup_backend):
        """Test creating response and retrieving it."""
        _, model, client, gateway = setup_backend

        # Create response
        create_resp = client.responses.create(model=model, input="Hello, world!")
        assert create_resp.id is not None
        assert create_resp.error is None
        assert create_resp.status == "completed"
        assert len(create_resp.output_text) > 0
        response_id = create_resp.id

        # Get response
        get_resp = client.responses.retrieve(response_id=response_id)
        assert get_resp.error is None
        assert get_resp.id == response_id
        assert get_resp.status == "completed"

        input_resp = client.responses.input_items.list(response_id=get_resp.id)
        assert input_resp.data is not None
        assert len(input_resp.data) > 0

    @pytest.mark.skip(reason="TODO: Add delete response feature")
    def test_delete_response(self, setup_backend):
        """Test deleting response."""
        _, model, client, gateway = setup_backend

        # Create response
        create_resp = client.responses.create(model=model, input="Test deletion")
        assert create_resp.id is not None
        assert create_resp.error is None
        assert create_resp.status == "completed"
        assert len(create_resp.output_text) > 0

        response_id = create_resp.id

        # Delete response
        client.responses.delete(response_id=response_id)

        # Verify it's deleted (should return 404)
        with pytest.raises(openai.NotFoundError):
            client.responses.retrieve(response_id=response_id)

    @pytest.mark.skip(reason="TODO: Add background response feature")
    def test_background_response(self, setup_backend):
        """Test background response execution."""
        _, model, client, gateway = setup_backend

        # Create background response
        create_resp = client.responses.create(
            model=model,
            input="Write a short story",
            background=True,
            max_output_tokens=100,
        )
        assert create_resp.id is not None
        assert create_resp.error is None
        assert create_resp.status in ["in_progress", "queued"]

        response_id = create_resp.id

        # Wait for completion
        final_data = wait_for_background_task(client, response_id, timeout=60)
        assert final_data.status == "completed"


# =============================================================================
# Response CRUD Tests (Oracle Storage)
# =============================================================================


@pytest.mark.storage("oracle")
@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
class TestResponseCRUDOracleStorage:
    """Tests for Response API CRUD operations with Oracle history backend."""

    def test_create_and_get_response(self, setup_backend):
        """Test creating response and retrieving it."""
        _, model, client, gateway = setup_backend

        # Create response
        create_resp = client.responses.create(model=model, input="Hello, world!")
        assert create_resp.id is not None
        assert create_resp.error is None
        assert create_resp.status == "completed"
        assert len(create_resp.output_text) > 0
        response_id = create_resp.id

        # Get response
        get_resp = client.responses.retrieve(response_id=response_id)
        assert get_resp.error is None
        assert get_resp.id == response_id
        assert get_resp.status == "completed"

        input_resp = client.responses.input_items.list(response_id=get_resp.id)
        assert input_resp.data is not None
        assert len(input_resp.data) > 0

    @pytest.mark.skip(reason="TODO: Add delete response feature")
    def test_delete_response(self, setup_backend):
        """Test deleting response."""
        _, model, client, gateway = setup_backend

        # Create response
        create_resp = client.responses.create(model=model, input="Test deletion")
        assert create_resp.id is not None
        assert create_resp.error is None
        assert create_resp.status == "completed"
        assert len(create_resp.output_text) > 0

        response_id = create_resp.id

        # Delete response
        client.responses.delete(response_id=response_id)

        # Verify it's deleted (should return 404)
        with pytest.raises(openai.NotFoundError):
            client.responses.retrieve(response_id=response_id)

    @pytest.mark.skip(reason="TODO: Add background response feature")
    def test_background_response(self, setup_backend):
        """Test background response execution."""
        _, model, client, gateway = setup_backend

        # Create background response
        create_resp = client.responses.create(
            model=model,
            input="Write a short story",
            background=True,
            max_output_tokens=100,
        )
        assert create_resp.id is not None
        assert create_resp.error is None
        assert create_resp.status in ["in_progress", "queued"]

        response_id = create_resp.id

        # Wait for completion
        final_data = wait_for_background_task(client, response_id, timeout=60)
        assert final_data.status == "completed"


# =============================================================================
# Conversation CRUD Tests (Memory Storage)
# =============================================================================


@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
class TestConversationCRUD:
    """Tests for Conversation API CRUD operations."""

    def test_create_and_get_conversation(self, setup_backend):
        """Test creating and retrieving conversation."""
        _, model, client, gateway = setup_backend

        # Create conversation
        create_resp = client.conversations.create(metadata={"user": "test_user"})
        assert create_resp.id is not None
        assert create_resp.created_at is not None

        create_data = create_resp.metadata
        assert create_data["user"] == "test_user"
        conversation_id = create_resp.id

        # Get conversation
        get_resp = client.conversations.retrieve(conversation_id=conversation_id)
        assert get_resp.id is not None
        assert get_resp.created_at is not None

        get_data = get_resp.metadata
        assert get_resp.id == conversation_id
        assert get_data["user"] == "test_user"

    def test_update_conversation(self, setup_backend):
        """Test updating conversation metadata."""
        _, model, client, gateway = setup_backend

        # Create conversation
        create_resp = client.conversations.create(metadata={"key1": "value1"})
        assert create_resp.id is not None
        assert create_resp.created_at is not None

        create_data = create_resp.metadata
        assert create_data["key1"] == "value1"
        assert "key2" not in create_data
        conversation_id = create_resp.id

        # Update conversation
        update_resp = client.conversations.update(
            conversation_id=conversation_id,
            metadata={"key1": "value1", "key2": "value2"},
        )
        assert update_resp.id == conversation_id
        update_data = update_resp.metadata
        assert update_data["key1"] == "value1"
        assert update_data["key2"] == "value2"

        # Verify update
        get_resp = client.conversations.retrieve(conversation_id=conversation_id)
        get_data = get_resp.metadata
        assert get_data["key1"] == "value1"
        assert get_data["key2"] == "value2"

    def test_delete_conversation(self, setup_backend):
        """Test deleting conversation."""
        _, model, client, gateway = setup_backend

        # Create conversation
        create_resp = client.conversations.create()
        assert create_resp.id is not None
        assert create_resp.created_at is not None
        conversation_id = create_resp.id

        # Delete conversation
        delete_resp = client.conversations.delete(conversation_id=conversation_id)
        assert delete_resp.id is not None
        assert delete_resp.deleted

        # Verify deletion
        with pytest.raises(openai.NotFoundError):
            client.conversations.retrieve(conversation_id=conversation_id)

    def test_list_conversation_items(self, setup_backend):
        """Test listing conversation items."""
        _, model, client, gateway = setup_backend

        # Create conversation
        conv_resp = client.conversations.create()
        assert conv_resp.id is not None
        conversation_id = conv_resp.id

        # Create response with conversation
        resp1 = client.responses.create(
            model=model,
            input="First message",
            conversation=conversation_id,
            max_output_tokens=50,
        )
        assert resp1.error is None

        resp2 = client.responses.create(
            model=model,
            input="Second message",
            conversation=conversation_id,
            max_output_tokens=50,
        )
        assert resp2.error is None

        # List items
        list_resp = client.conversations.items.list(conversation_id=conversation_id)
        assert list_resp is not None
        assert list_resp.data is not None

        list_data = list_resp.data
        # Should have at least 4 items (2 inputs + 2 outputs)
        assert len(list_data) >= 4


# =============================================================================
# Conversation CRUD Tests (Oracle Storage)
# =============================================================================


@pytest.mark.storage("oracle")
@pytest.mark.parametrize("setup_backend", ["openai"], indirect=True)
class TestConversationCRUDOracleStorage:
    """Tests for Conversation API CRUD operations with Oracle history backend."""

    def test_create_and_get_conversation(self, setup_backend):
        """Test creating and retrieving conversation."""
        _, model, client, gateway = setup_backend

        # Create conversation
        create_resp = client.conversations.create(metadata={"user": "test_user"})
        assert create_resp.id is not None
        assert create_resp.created_at is not None

        create_data = create_resp.metadata
        assert create_data["user"] == "test_user"
        conversation_id = create_resp.id

        # Get conversation
        get_resp = client.conversations.retrieve(conversation_id=conversation_id)
        assert get_resp.id is not None
        assert get_resp.created_at is not None

        get_data = get_resp.metadata
        assert get_resp.id == conversation_id
        assert get_data["user"] == "test_user"

    def test_update_conversation(self, setup_backend):
        """Test updating conversation metadata."""
        _, model, client, gateway = setup_backend

        # Create conversation
        create_resp = client.conversations.create(metadata={"key1": "value1"})
        assert create_resp.id is not None
        assert create_resp.created_at is not None

        create_data = create_resp.metadata
        assert create_data["key1"] == "value1"
        assert "key2" not in create_data
        conversation_id = create_resp.id

        # Update conversation
        update_resp = client.conversations.update(
            conversation_id=conversation_id,
            metadata={"key1": "value1", "key2": "value2"},
        )
        assert update_resp.id == conversation_id
        update_data = update_resp.metadata
        assert update_data["key1"] == "value1"
        assert update_data["key2"] == "value2"

        # Verify update
        get_resp = client.conversations.retrieve(conversation_id=conversation_id)
        get_data = get_resp.metadata
        assert get_data["key1"] == "value1"
        assert get_data["key2"] == "value2"

    def test_delete_conversation(self, setup_backend):
        """Test deleting conversation."""
        _, model, client, gateway = setup_backend

        # Create conversation
        create_resp = client.conversations.create()
        assert create_resp.id is not None
        assert create_resp.created_at is not None
        conversation_id = create_resp.id

        # Delete conversation
        delete_resp = client.conversations.delete(conversation_id=conversation_id)
        assert delete_resp.id is not None
        assert delete_resp.deleted

        # Verify deletion
        with pytest.raises(openai.NotFoundError):
            client.conversations.retrieve(conversation_id=conversation_id)

    def test_list_conversation_items(self, setup_backend):
        """Test listing conversation items."""
        _, model, client, gateway = setup_backend

        # Create conversation
        conv_resp = client.conversations.create()
        assert conv_resp.id is not None
        conversation_id = conv_resp.id

        # Create response with conversation
        resp1 = client.responses.create(
            model=model,
            input="First message",
            conversation=conversation_id,
            max_output_tokens=50,
        )
        assert resp1.error is None

        resp2 = client.responses.create(
            model=model,
            input="Second message",
            conversation=conversation_id,
            max_output_tokens=50,
        )
        assert resp2.error is None

        # List items
        list_resp = client.conversations.items.list(conversation_id=conversation_id)
        assert list_resp is not None
        assert list_resp.data is not None

        list_data = list_resp.data
        # Should have at least 4 items (2 inputs + 2 outputs)
        assert len(list_data) >= 4
