import pytest
from openai import OpenAIError

from semantic_router.encoders import OpenAIEncoder


@pytest.fixture
def openai_encoder(mocker):
    mocker.patch("openai.Client")
    return OpenAIEncoder(openai_api_key="test_api_key")


class TestOpenAIEncoder:
    def test_openai_encoder_init_success(self, mocker):
        mocker.patch("os.getenv", return_value="fake-api-key")
        encoder = OpenAIEncoder()
        assert encoder.client is not None

    def test_openai_encoder_init_no_api_key(self, mocker):
        mocker.patch("os.getenv", return_value=None)
        with pytest.raises(ValueError) as e:
            OpenAIEncoder()
        assert "OpenAI API key cannot be 'None'." in str(e.value)

    def test_openai_encoder_call_uninitialized_client(self, openai_encoder):
        # Set the client to None to simulate an uninitialized client
        openai_encoder.client = None
        with pytest.raises(ValueError) as e:
            openai_encoder(["test document"])
        assert "OpenAI client is not initialized." in str(e.value)

    def test_openai_encoder_init_exception(self, mocker):
        mocker.patch("os.getenv", return_value="fake-api-key")
        mocker.patch("openai.Client", side_effect=Exception("Initialization error"))
        with pytest.raises(ValueError) as e:
            OpenAIEncoder()
        assert (
            "OpenAI API client failed to initialize. Error: Initialization error"
            in str(e.value)
        )

    def test_openai_encoder_call_success(self, openai_encoder, mocker):
        mocker.patch("os.getenv", return_value="fake-api-key")
        mocker.patch.object(
            openai_encoder.client.embeddings,
            "create",
            return_value={"data": [{"embedding": [0.1, 0.2]}]},
        )
        embeddings = openai_encoder(["test document"])
        assert embeddings == [[0.1, 0.2]]

    def test_openai_encoder_call_with_retries(self, openai_encoder, mocker):
        mocker.patch("os.getenv", return_value="fake-api-key")
        mocker.patch("time.sleep", return_value=None)  # To speed up the test
        mocker.patch.object(
            openai_encoder.client.embeddings,
            "create",
            side_effect=OpenAIError("Test error"),
        )
        with pytest.raises(ValueError) as e:
            openai_encoder(["test document"])
        assert "No embeddings returned. Error: Test error" in str(e.value)

    def test_openai_encoder_call_failure_non_openai_error(self, openai_encoder, mocker):
        mocker.patch("os.getenv", return_value="fake-api-key")
        mocker.patch("time.sleep", return_value=None)  # To speed up the test
        mocker.patch.object(
            openai_encoder.client.embeddings,
            "create",
            side_effect=Exception("Non-OpenAIError"),
        )
        with pytest.raises(ValueError) as e:
            openai_encoder(["test document"])

        assert "OpenAI API call failed. Error: Non-OpenAIError" in str(e.value)

    def test_openai_encoder_call_successful_retry(self, openai_encoder, mocker):
        mocker.patch("os.getenv", return_value="fake-api-key")
        mocker.patch("time.sleep", return_value=None)  # To speed up the test
        responses = [OpenAIError("Test error"), {"data": [{"embedding": [0.1, 0.2]}]}]
        mocker.patch.object(
            openai_encoder.client.embeddings, "create", side_effect=responses
        )
        embeddings = openai_encoder(["test document"])
        assert embeddings == [[0.1, 0.2]]