#  Licensed to Elasticsearch B.V. under one or more contributor
#  license agreements. See the NOTICE file distributed with
#  this work for additional information regarding copyright
#  ownership. Elasticsearch B.V. licenses this file to you under
#  the Apache License, Version 2.0 (the "License"); you may
#  not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
# 	http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing,
#  software distributed under the License is distributed on an
#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#  KIND, either express or implied.  See the License for the
#  specific language governing permissions and limitations
#  under the License.

from typing import List

from elastic_transport import AsyncTransport

from elasticsearch.helpers.vectorstore import AsyncEmbeddingService


class AsyncRequestSavingTransport(AsyncTransport):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.requests: list = []

    async def perform_request(self, *args, **kwargs):
        self.requests.append(kwargs)
        return await super().perform_request(*args, **kwargs)


class AsyncFakeEmbeddings(AsyncEmbeddingService):
    """Fake embeddings functionality for testing."""

    def __init__(self, dimensionality: int = 10) -> None:
        self.dimensionality = dimensionality

    async def embed_documents(self, texts: List[str]) -> List[List[float]]:
        """Return simple embeddings. Embeddings encode each text as its index."""
        return [
            [float(1.0)] * (self.dimensionality - 1) + [float(i)]
            for i in range(len(texts))
        ]

    async def embed_query(self, query: str) -> List[float]:
        """Return constant query embeddings.
        Embeddings are identical to embed_documents(texts)[0].
        Distance to each text will be that text's index,
        as it was passed to embed_documents.
        """
        return [float(1.0)] * (self.dimensionality - 1) + [float(0.0)]


class AsyncConsistentFakeEmbeddings(AsyncFakeEmbeddings):
    """Fake embeddings which remember all the texts seen so far to return consistent
    vectors for the same texts."""

    def __init__(self, dimensionality: int = 10) -> None:
        self.known_texts: List[str] = []
        self.dimensionality = dimensionality

    async def embed_documents(self, texts: List[str]) -> List[List[float]]:
        """Return consistent embeddings for each text seen so far."""
        out_vectors = []
        for text in texts:
            if text not in self.known_texts:
                self.known_texts.append(text)
            vector = [float(1.0)] * (self.dimensionality - 1) + [
                float(self.known_texts.index(text) + 1)
            ]
            out_vectors.append(vector)
        return out_vectors

    async def embed_query(self, query: str) -> List[float]:
        """Return consistent embeddings for the text, if seen before, or a constant
        one if the text is unknown."""
        result = await self.embed_documents([query])
        return result[0]
