<?php

declare(strict_types=1);

namespace App\Services;

use App\Jobs\DeleteArtworkFromIndexJob;
use App\Jobs\IndexArtworkJob;
use App\Models\Artwork;
use Closure;
use Illuminate\Support\Facades\Log;

/**
 * Manages Meilisearch index operations for artworks.
 *
 * All write operations are dispatched to queues — never block requests.
 */
final class ArtworkSearchIndexer
{
    /**
     * Queue an artwork for indexing (insert or update).
     */
    public function index(Artwork $artwork): void
    {
        IndexArtworkJob::dispatch($artwork->id);
    }

    /**
     * Queue an artwork for re-indexing after an update.
     */
    public function update(Artwork $artwork): void
    {
        IndexArtworkJob::dispatch($artwork->id);
    }

    /**
     * Queue removal of an artwork from the index.
     */
    public function delete(int $id): void
    {
        DeleteArtworkFromIndexJob::dispatch($id);
    }

    /**
     * Rebuild the entire artworks index in background chunks.
     * Run via: php artisan artworks:search-rebuild
     *
     * @param  Closure(int, int, int, int, int, int): void|null  $onChunk
     * @return array{total:int, dispatched:int, chunks:int}
     */
    public function rebuildAll(int $chunkSize = 500, ?Closure $onChunk = null, bool $reverse = false, ?int $limit = null): array
    {
        $query = Artwork::query()
            ->public()
            ->published();

        if ($reverse) {
            $query->orderByDesc('id');
        } else {
            $query->orderBy('id');
        }

        if ($limit !== null) {
            $query->limit($limit);
        }

        $total = (clone $query)->count();

        $dispatched = 0;
        $chunks = 0;

        $query
            ->with(['user', 'tags', 'categories', 'stats', 'awardStat'])
            ->chunk($chunkSize, function ($artworks) use (&$chunks, &$dispatched, $total, $onChunk): void {
                $chunks++;

                $count = $artworks->count();
                $firstId = (int) ($artworks->first()?->id ?? 0);
                $lastId = (int) ($artworks->last()?->id ?? 0);

                foreach ($artworks as $artwork) {
                    IndexArtworkJob::dispatch($artwork->id);
                    $dispatched++;
                }

                if ($onChunk !== null) {
                    $onChunk($chunks, $count, $dispatched, $total, $firstId, $lastId);
                }
            });

        Log::info('ArtworkSearchIndexer::rebuildAll — jobs dispatched', [
            'total' => $total,
            'dispatched' => $dispatched,
            'chunks' => $chunks,
            'chunk_size' => $chunkSize,
            'reverse' => $reverse,
            'limit' => $limit,
        ]);

        return [
            'total' => $total,
            'dispatched' => $dispatched,
            'chunks' => $chunks,
        ];
    }
}
