<?php

declare(strict_types=1);

namespace App\Services\Vision;

use Illuminate\Support\Facades\Http;

final class ArtworkEmbeddingClient
{
    /**
     * @return array<int, float>
     */
    public function embed(string $imageUrl, int $artworkId, string $sourceHash): array
    {
        $base = trim((string) config('vision.clip.base_url', ''));
        if ($base === '') {
            return [];
        }

        $endpoint = (string) config('recommendations.embedding.endpoint', '/embed');
        $url = rtrim($base, '/') . '/' . ltrim($endpoint, '/');

        $timeout = (int) config('recommendations.embedding.timeout_seconds', 8);
        $connectTimeout = (int) config('recommendations.embedding.connect_timeout_seconds', 2);
        $retries = (int) config('recommendations.embedding.retries', 1);
        $delay = (int) config('recommendations.embedding.retry_delay_ms', 200);

        $response = Http::acceptJson()
            ->connectTimeout(max(1, $connectTimeout))
            ->timeout(max(1, $timeout))
            ->retry(max(0, $retries), max(0, $delay), throw: false)
            ->post($url, [
                'image_url' => $imageUrl,
                'artwork_id' => $artworkId,
                'hash' => $sourceHash,
            ]);

        if (! $response->ok()) {
            return [];
        }

        return $this->extractEmbedding($response->json());
    }

    /**
     * @param mixed $json
     * @return array<int, float>
     */
    private function extractEmbedding(mixed $json): array
    {
        $candidate = null;

        if (is_array($json) && $this->isNumericVector($json)) {
            $candidate = $json;
        } elseif (is_array($json) && isset($json['embedding']) && is_array($json['embedding'])) {
            $candidate = $json['embedding'];
        } elseif (is_array($json) && isset($json['data']['embedding']) && is_array($json['data']['embedding'])) {
            $candidate = $json['data']['embedding'];
        }

        if (! is_array($candidate) || ! $this->isNumericVector($candidate)) {
            return [];
        }

        $vector = array_map(static fn ($value): float => (float) $value, $candidate);
        $dim = count($vector);

        $minDim = (int) config('recommendations.embedding.min_dim', 64);
        $maxDim = (int) config('recommendations.embedding.max_dim', 4096);
        if ($dim < $minDim || $dim > $maxDim) {
            return [];
        }

        return $vector;
    }

    /**
     * @param array<mixed> $arr
     */
    private function isNumericVector(array $arr): bool
    {
        if ($arr === []) {
            return false;
        }

        foreach ($arr as $value) {
            if (! is_numeric($value)) {
                return false;
            }
        }

        return true;
    }
}
