<?php

declare(strict_types=1);

namespace App\Services\Vision;

use App\Models\Artwork;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use RuntimeException;

final class AiArtworkVectorSearchService
{
    public function __construct(
        private readonly VectorGatewayClient $client,
        private readonly ArtworkVisionImageUrl $imageUrl,
    ) {
    }

    public function isConfigured(): bool
    {
        return $this->client->isConfigured();
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function similarToArtwork(Artwork $artwork, int $limit = 12): array
    {
        $safeLimit = max(1, min(24, $limit));
        $cacheKey = sprintf('rec:artwork:%d:similar-ai:%d', $artwork->id, $safeLimit);
        $ttl = max(60, (int) config('recommendations.ttl.similar_artworks', 30 * 60));

        return Cache::remember($cacheKey, $ttl, function () use ($artwork, $safeLimit): array {
            $url = $this->imageUrl->fromArtwork($artwork);
            if ($url === null) {
                return [];
            }

            $matches = $this->client->searchByUrl($url, $safeLimit + 1);

            return $this->resolveMatches($matches, $safeLimit, $artwork->id);
        });
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function searchByUploadedImage(UploadedFile $file, int $limit = 12): array
    {
        $safeLimit = max(1, min(24, $limit));
        $path = $file->store('ai-search/tmp', 'public');

        if (! is_string($path) || $path === '') {
            throw new RuntimeException('Unable to persist uploaded image for vector search.');
        }

        $publicBaseUrl = rtrim((string) config('filesystems.disks.public.url', ''), '/');
        if ($publicBaseUrl === '') {
            Storage::disk('public')->delete($path);
            throw new RuntimeException('Public disk URL is not configured for vector search uploads.');
        }

        $url = $publicBaseUrl . '/' . ltrim($path, '/');

        try {
            $matches = $this->client->searchByUrl($url, $safeLimit);

            return $this->resolveMatches($matches, $safeLimit);
        } finally {
            Storage::disk('public')->delete($path);
        }
    }

    /**
     * @param list<array{id: int|string, score: float, metadata: array<string, mixed>}> $matches
     * @return list<array<string, mixed>>
     */
    private function resolveMatches(array $matches, int $limit, ?int $excludeArtworkId = null): array
    {
        $orderedIds = [];
        $scores = [];

        foreach ($matches as $match) {
            $artworkId = (int) ($match['id'] ?? 0);
            if ($artworkId <= 0) {
                continue;
            }

            if ($excludeArtworkId !== null && $artworkId === $excludeArtworkId) {
                continue;
            }

            if (isset($scores[$artworkId])) {
                continue;
            }

            $orderedIds[] = $artworkId;
            $scores[$artworkId] = (float) ($match['score'] ?? 0.0);
        }

        if ($orderedIds === []) {
            return [];
        }

        $artworks = Artwork::query()
            ->whereIn('id', $orderedIds)
            ->public()
            ->published()
            ->with(['user:id,name', 'user.profile:user_id,avatar_hash'])
            ->get()
            ->keyBy('id');

        $items = [];
        foreach ($orderedIds as $artworkId) {
            /** @var Artwork|null $artwork */
            $artwork = $artworks->get($artworkId);
            if ($artwork === null) {
                continue;
            }

            $items[] = [
                'id' => $artwork->id,
                'title' => $artwork->title,
                'slug' => $artwork->slug,
                'thumb' => $artwork->thumbUrl('md'),
                'url' => '/art/' . $artwork->id . '/' . $artwork->slug,
                'author' => $artwork->user?->name ?? 'Artist',
                'author_avatar' => $artwork->user?->profile?->avatar_url,
                'author_id' => $artwork->user_id,
                'score' => round((float) ($scores[$artworkId] ?? 0.0), 5),
                'source' => 'vector_gateway',
            ];

            if (count($items) >= $limit) {
                break;
            }
        }

        return $items;
    }
}
