<?php

declare(strict_types=1);

namespace App\Services\Recommendations\VectorSimilarity;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * pgvector adapter — uses the artwork_embeddings table with cosine similarity.
 *
 * Requires PostgreSQL with the pgvector extension installed.
 * Schema: artwork_embeddings (artwork_id PK, model, dims, embedding vector(N), ...)
 *
 * Spec §9 Option A.
 */
final class PgvectorAdapter implements VectorAdapterInterface
{
    public function querySimilar(int $artworkId, int $topK = 100): array
    {
        // Fetch reference embedding
        $ref = DB::table('artwork_embeddings')
            ->where('artwork_id', $artworkId)
            ->select('embedding_json')
            ->first();

        if (! $ref || ! $ref->embedding_json) {
            return [];
        }

        $embedding = json_decode($ref->embedding_json, true);
        if (! is_array($embedding) || $embedding === []) {
            return [];
        }

        // pgvector cosine distance operator: <=>
        // Score = 1 - distance  (higher = more similar)
        $vecLiteral = '[' . implode(',', array_map('floatval', $embedding)) . ']';

        try {
            $rows = DB::select(
                "SELECT artwork_id, 1 - (embedding_json::vector <=> ?::vector) AS score
                 FROM artwork_embeddings
                 WHERE artwork_id != ?
                 ORDER BY embedding_json::vector <=> ?::vector
                 LIMIT ?",
                [$vecLiteral, $artworkId, $vecLiteral, $topK]
            );
        } catch (\Throwable $e) {
            Log::warning("[PgvectorAdapter] Query failed: {$e->getMessage()}");
            return [];
        }

        return array_map(fn ($row) => [
            'artwork_id' => (int) $row->artwork_id,
            'score'      => (float) $row->score,
        ], $rows);
    }

    public function upsertEmbedding(int $artworkId, array $embedding, array $metadata = []): void
    {
        $json = json_encode($embedding);

        DB::table('artwork_embeddings')->updateOrInsert(
            ['artwork_id' => $artworkId],
            [
                'embedding_json' => $json,
                'model'          => $metadata['model'] ?? 'clip',
                'model_version'  => $metadata['model_version'] ?? 'v1',
                'dim'            => count($embedding),
                'is_normalized'  => $metadata['is_normalized'] ?? true,
                'generated_at'   => now(),
            ],
        );
    }

    public function deleteEmbedding(int $artworkId): void
    {
        DB::table('artwork_embeddings')
            ->where('artwork_id', $artworkId)
            ->delete();
    }
}
