from unittest.mock import MagicMock, Mock, patch

import numpy as np
import pytest
import torch
from neo4j_graphrag.embeddings.base import Embedder
from neo4j_graphrag.embeddings.sentence_transformers import (
    SentenceTransformerEmbeddings,
)
from neo4j_graphrag.exceptions import EmbeddingsGenerationError


def get_mock_sentence_transformers() -> MagicMock:
    mock = MagicMock()
    # I know, I know... ¯\_(ツ)_/¯
    # This is to cover the if type checks in the embed_query method
    mock.Tensor = torch.Tensor
    mock.ndarray = np.ndarray
    return mock


@patch("builtins.__import__")
def test_initialization(mock_import: Mock) -> None:
    MockSentenceTransformer = get_mock_sentence_transformers()
    mock_import.return_value = MockSentenceTransformer
    instance = SentenceTransformerEmbeddings()
    MockSentenceTransformer.SentenceTransformer.assert_called_with("all-MiniLM-L6-v2")
    assert isinstance(instance, Embedder)


@patch("builtins.__import__")
def test_initialization_with_custom_model(mock_import: Mock) -> None:
    MockSentenceTransformer = get_mock_sentence_transformers()
    mock_import.return_value = MockSentenceTransformer
    custom_model = "distilbert-base-nli-stsb-mean-tokens"
    SentenceTransformerEmbeddings(model=custom_model)
    MockSentenceTransformer.SentenceTransformer.assert_called_with(custom_model)


@patch("builtins.__import__")
def test_embed_query(mock_import: Mock) -> None:
    MockSentenceTransformer = get_mock_sentence_transformers()
    mock_import.return_value = MockSentenceTransformer
    mock_model = MockSentenceTransformer.SentenceTransformer.return_value
    mock_model.encode.return_value = np.array([[0.1, 0.2, 0.3]])

    instance = SentenceTransformerEmbeddings()
    result = instance.embed_query("test query")

    mock_model.encode.assert_called_with(["test query"])
    assert isinstance(result, list)
    assert result == [0.1, 0.2, 0.3]


@patch("builtins.__import__", side_effect=ImportError)
def test_import_error(mock_import: Mock) -> None:
    with pytest.raises(ImportError):
        SentenceTransformerEmbeddings()


@patch("builtins.__import__")
def test_embed_query_non_retryable_error_handling(mock_import: Mock) -> None:
    """Test that non-retryable errors fail immediately without retries."""
    MockSentenceTransformer = get_mock_sentence_transformers()
    mock_import.return_value = MockSentenceTransformer
    mock_model = MockSentenceTransformer.SentenceTransformer.return_value
    mock_model.encode.side_effect = Exception("Model error")

    instance = SentenceTransformerEmbeddings()
    with pytest.raises(
        EmbeddingsGenerationError,
        match="Failed to generate embedding with SentenceTransformer",
    ):
        instance.embed_query("test query")

    # Verify the model was called only once (no retries for non-rate-limit errors)
    assert mock_model.encode.call_count == 1
