<?php

declare(strict_types=1);

namespace Mtc\Plugins\Agent\Agents\VectorStore;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Illuminate\Support\Facades\Log;
use NeuronAI\RAG\Document;
use NeuronAI\RAG\VectorStore\VectorStoreInterface;

class PineconeVectorStore implements VectorStoreInterface
{
    protected Client $client;

    protected array $filters = [];

    public function __construct(
        string $key,
        protected string $indexUrl,
        protected int $topK = 4,
        string $version = '2025-04',
        protected string $namespace = '__default__'
    ) {
        $this->client = new Client([
            'base_uri' => \trim($this->indexUrl, '/').'/',
            'headers' => [
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
                'Api-Key' => $key,
                'X-Pinecone-API-Version' => $version,
            ]
        ]);
    }

    public function addDocument(Document $document): VectorStoreInterface
    {
        return $this->addDocuments([$document]);
    }

    public function addDocuments(array $documents): VectorStoreInterface
    {
        try {
            $this->client->post("vectors/upsert", [
                RequestOptions::JSON => [
                    'namespace' => $this->namespace,
                    'vectors' => \array_map(fn (Document $document): array => [
                        'id' => (string) $document->getId(),
                        'values' => $document->getEmbedding(),
                        'metadata' => [
                            'content' => $document->getContent(),
                            'sourceType' => $document->getSourceType(),
                            'sourceName' => $document->getSourceName(),
                            ...$document->metadata,
                        ],
                    ], $documents)
                ]
            ]);
        } catch (GuzzleException $e) {
            Log::error('Pinecone upsert failed: ' . $e->getMessage());
            throw new \RuntimeException('Failed to store documents in vector database.', 0, $e);
        }

        return $this;
    }

    public function deleteBySource(string $sourceType, string $sourceName): VectorStoreInterface
    {
        try {
            $this->client->post("vectors/delete", [
                RequestOptions::JSON => [
                    'namespace' => $this->namespace,
                    'filter' => [
                        'sourceType' => ['$eq' => $sourceType],
                        'sourceName' => ['$eq' => $sourceName],
                    ]
                ]
            ]);
        } catch (GuzzleException $e) {
            Log::error('Pinecone delete failed: ' . $e->getMessage());
            throw new \RuntimeException('Failed to delete documents from vector database.', 0, $e);
        }

        return $this;
    }

    public function similaritySearch(array $embedding): iterable
    {
        $payload = [
            'namespace' => $this->namespace,
            'includeMetadata' => true,
            'includeValues' => true,
            'vector' => $embedding,
            'topK' => $this->topK,
        ];

        // Only include filter if it's not empty
        if (!empty($this->filters)) {
            $payload['filter'] = $this->filters;
        }

        Log::info('Pinecone: Searching vector DB', [
            'namespace' => $this->namespace,
            'topK' => $this->topK,
            'embedding_length' => count($embedding),
        ]);

        try {
            $result = $this->client->post("query", [
                RequestOptions::JSON => $payload
            ])->getBody()->getContents();
        } catch (GuzzleException $e) {
            Log::error('Pinecone similarity search failed: ' . $e->getMessage());
            return [];
        }

        $result = \json_decode($result, true);

        if (!isset($result['matches']) || !is_array($result['matches'])) {
            Log::warning('Pinecone: No matches found in response');
            return [];
        }

        Log::info('Pinecone: Search results', [
            'matches_count' => count($result['matches']),
            'matches' => array_map(fn($m) => [
                'id' => $m['id'],
                'score' => $m['score'],
                'content_preview' => substr($m['metadata']['content'] ?? '', 0, 100),
            ], $result['matches']),
        ]);

        return \array_map(function (array $item): Document {
            $document = new Document();
            $document->id = $item['id'];
            $document->embedding = $item['values'];
            $document->content = $item['metadata']['content'];
            $document->sourceType = $item['metadata']['sourceType'];
            $document->sourceName = $item['metadata']['sourceName'];
            $document->score = $item['score'];

            foreach ($item['metadata'] as $name => $value) {
                if (!\in_array($name, ['content', 'sourceType', 'sourceName'])) {
                    $document->addMetadata($name, $value);
                }
            }

            return $document;
        }, $result['matches']);
    }

    public function withFilters(array $filters): self
    {
        $this->filters = $filters;
        return $this;
    }
}
