<?php

declare(strict_types=1);

namespace App\Services\Recommendations\VectorSimilarity;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * Managed vector DB adapter (Pinecone-style REST API).
 *
 * Spec §9 Option B.
 *
 * Configuration:
 *   recommendations.similarity.pinecone.api_key
 *   recommendations.similarity.pinecone.index_host
 *   recommendations.similarity.pinecone.index_name
 *   recommendations.similarity.pinecone.namespace
 *   recommendations.similarity.pinecone.top_k
 */
final class PineconeAdapter implements VectorAdapterInterface
{
    private function apiKey(): string
    {
        return (string) config('recommendations.similarity.pinecone.api_key', '');
    }

    private function host(): string
    {
        return rtrim((string) config('recommendations.similarity.pinecone.index_host', ''), '/');
    }

    private function namespace(): string
    {
        return (string) config('recommendations.similarity.pinecone.namespace', '');
    }

    public function querySimilar(int $artworkId, int $topK = 100): array
    {
        $configTopK = (int) config('recommendations.similarity.pinecone.top_k', 100);
        $effectiveTopK = min($topK, $configTopK);

        $vectorId = "artwork:{$artworkId}";

        try {
            $response = Http::withHeaders([
                'Api-Key'      => $this->apiKey(),
                'Content-Type' => 'application/json',
            ])->timeout(10)->post("{$this->host()}/query", array_filter([
                'id'              => $vectorId,
                'topK'            => $effectiveTopK + 1, // +1 to exclude self
                'includeMetadata' => true,
                'namespace'       => $this->namespace() ?: null,
                'filter'          => [
                    'is_active' => ['$eq' => true],
                ],
            ]));

            if (! $response->successful()) {
                Log::warning("[PineconeAdapter] Query failed: HTTP {$response->status()}");
                return [];
            }

            $matches = $response->json('matches', []);

            $results = [];
            foreach ($matches as $match) {
                $matchId = $match['id'] ?? '';
                // Extract artwork ID from "artwork:123" format
                if (! str_starts_with($matchId, 'artwork:')) {
                    continue;
                }
                $matchArtworkId = (int) substr($matchId, 8);
                if ($matchArtworkId === $artworkId) {
                    continue; // skip self
                }

                $results[] = [
                    'artwork_id' => $matchArtworkId,
                    'score'      => (float) ($match['score'] ?? 0.0),
                ];
            }

            return $results;
        } catch (\Throwable $e) {
            Log::warning("[PineconeAdapter] Query exception: {$e->getMessage()}");
            return [];
        }
    }

    public function upsertEmbedding(int $artworkId, array $embedding, array $metadata = []): void
    {
        $vectorId = "artwork:{$artworkId}";

        // Spec §9B: metadata should include category_id, content_type, author_id, is_active, nsfw
        $pineconeMetadata = array_merge([
            'is_active'    => true,
            'category_id'  => $metadata['category_id'] ?? null,
            'content_type' => $metadata['content_type'] ?? null,
            'author_id'    => $metadata['author_id'] ?? null,
            'nsfw'         => $metadata['nsfw'] ?? false,
        ], array_diff_key($metadata, array_flip([
            'category_id', 'content_type', 'author_id', 'nsfw', 'is_active',
        ])));

        // Remove null values (Pinecone doesn't accept nulls in metadata)
        $pineconeMetadata = array_filter($pineconeMetadata, fn ($v) => $v !== null);

        try {
            $response = Http::withHeaders([
                'Api-Key'      => $this->apiKey(),
                'Content-Type' => 'application/json',
            ])->timeout(10)->post("{$this->host()}/vectors/upsert", array_filter([
                'vectors'   => [
                    [
                        'id'       => $vectorId,
                        'values'   => array_map('floatval', $embedding),
                        'metadata' => $pineconeMetadata,
                    ],
                ],
                'namespace' => $this->namespace() ?: null,
            ]));

            if (! $response->successful()) {
                Log::warning("[PineconeAdapter] Upsert failed for artwork {$artworkId}: HTTP {$response->status()}");
            }
        } catch (\Throwable $e) {
            Log::warning("[PineconeAdapter] Upsert exception for artwork {$artworkId}: {$e->getMessage()}");
        }
    }

    public function deleteEmbedding(int $artworkId): void
    {
        $vectorId = "artwork:{$artworkId}";

        try {
            Http::withHeaders([
                'Api-Key'      => $this->apiKey(),
                'Content-Type' => 'application/json',
            ])->timeout(10)->post("{$this->host()}/vectors/delete", array_filter([
                'ids'       => [$vectorId],
                'namespace' => $this->namespace() ?: null,
            ]));
        } catch (\Throwable $e) {
            Log::warning("[PineconeAdapter] Delete exception for artwork {$artworkId}: {$e->getMessage()}");
        }
    }
}
