<?php
namespace App\Jobs;

use App\Services\ArtworkStatsService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class IncrementArtworkView implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $artworkId;
    public int $count;
    public string $eventId;

    /**
     * Require a unique event id to make the job idempotent across retries and concurrency.
     *
     * @param int $artworkId
     * @param string $eventId  Unique identifier for this view event (caller must supply)
     * @param int $count
     */
    public function __construct(int $artworkId, string $eventId, int $count = 1)
    {
        $this->artworkId = $artworkId;
        $this->count = max(1, $count);
        $this->eventId = $eventId;
    }

    /**
     * Execute the job.
     * Uses Redis setnx to ensure only one worker processes a given eventId.
     * Delegates actual DB mutation to ArtworkStatsService which uses transactions.
     */
    public function handle(ArtworkStatsService $statsService): void
    {
        $key = 'artwork:view:processed:' . $this->eventId;

        try {
            $didSet = false;
            try {
                $didSet = Redis::setnx($key, 1);
                if ($didSet) {
                    // expire after 1 day to limit key growth
                    Redis::expire($key, 86400);
                }
            } catch (\Throwable $e) {
                Log::warning('Redis unavailable for IncrementArtworkView; proceeding without dedupe', ['error' => $e->getMessage()]);
                // If Redis is not available, fall back to applying delta directly.
                // This sacrifices idempotency but ensures metrics are recorded.
                $statsService->applyDelta($this->artworkId, ['views' => $this->count]);
                return;
            }

            if (! $didSet) {
                // Already processed this eventId — idempotent skip
                return;
            }

            // Safe increment using transactional method
            $statsService->applyDelta($this->artworkId, ['views' => $this->count]);
        } catch (\Throwable $e) {
            Log::error('IncrementArtworkView job failed', ['artwork_id' => $this->artworkId, 'event_id' => $this->eventId, 'error' => $e->getMessage()]);
            // Let the job be retried by throwing
            throw $e;
        }
    }
}
