<?php

namespace App\Jobs;

use App\Helpers\FirstParty\Credits\CreditsService;
use App\Helpers\FirstParty\Meme\MemeGenerator;
use App\Models\UserMemeGeneration;
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\Cache;
use Illuminate\Support\Str;

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

    public $timeout = 30;

    public $tries = 1;

    protected $userId;

    protected $prompt;

    protected $jobId;

    public function __construct(int $userId, string $prompt, ?string $jobId)
    {
        $this->userId = $userId;
        $this->prompt = $prompt;
        $this->jobId = $jobId ?: Str::uuid()->toString();
        $this->queue = 'ai';
    }

    public function handle(): void
    {
        $userGeneration = UserMemeGeneration::where('job_id', $this->jobId)->first();

        if (! $userGeneration) {
            throw new \Exception("User generation record not found for job {$this->jobId}");
        }

        try {
            // Update status to processing
            $userGeneration->update(['status' => 'processing']);
            Cache::put("meme_job_status_{$this->jobId}", 'processing', 300);

            $meme = MemeGenerator::generateMemeByKeyword($this->prompt);
            $meme_media = MemeGenerator::getSuitableMemeMedia($meme, 2);

            // Update the generation record with the meme
            $userGeneration->update([
                'meme_id' => $meme->id,
                'status' => 'completed',
                'credits_are_processed' => true,
            ]);

            $result = [
                'generate' => [
                    'info' => $meme,
                    'caption' => $meme->caption,
                    'meme' => $meme_media,
                    'background' => $meme->background_media,
                ],
            ];

            Cache::put("meme_job_result_{$this->jobId}", $result, 300);
            Cache::put("meme_job_status_{$this->jobId}", 'completed', 300);

            // Clear active job from cache
            Cache::forget("user_active_job_{$this->userId}");
        } catch (\Exception $e) {
            // Handle failure with credit refund
            if (! $userGeneration->credits_are_processed) {
                if ($userGeneration->credits_to_be_charged > 0) {
                    CreditsService::depositAlacarte(
                        $userGeneration->user_id,
                        $userGeneration->credits_to_be_charged,
                        'Refunded credits for failed generation due to provider issue.'
                    );
                }

                $userGeneration->update([
                    'credits_are_processed' => true,
                    'status' => 'failed',
                ]);
            }

            Cache::put("meme_job_status_{$this->jobId}", 'failed', 300);
            Cache::put("meme_job_error_{$this->jobId}", $e->getMessage(), 300);

            // Clear active job from cache
            Cache::forget("user_active_job_{$this->userId}");

            throw $e;
        }
    }

    public function failed(\Throwable $exception): void
    {
        $userGeneration = UserMemeGeneration::where('job_id', $this->jobId)->first();

        if ($userGeneration && ! $userGeneration->credits_are_processed) {
            if ($userGeneration->credits_to_be_charged > 0) {
                CreditsService::depositAlacarte(
                    $userGeneration->user_id,
                    $userGeneration->credits_to_be_charged,
                    'Refunded credits for failed generation due to system error.'
                );
            }

            $userGeneration->update([
                'credits_are_processed' => true,
                'status' => 'failed',
            ]);
        }

        Cache::put("meme_job_status_{$this->jobId}", 'failed', 300);
        Cache::forget("user_active_job_{$this->userId}");
    }

    public function getJobId(): string
    {
        return $this->jobId;
    }
}
