<?php

namespace App\Jobs;

use App\Models\Conversation;
use App\Services\GoogleAiService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Broadcast;

class GenerateImageJob implements ShouldQueue
{
	use Queueable, InteractsWithQueue, SerializesModels;
	
	public $tries = 4; // Increased from 3 to 4 for better success rate
	public $timeout = 300; // 5 minutes
	public $backoff = [10, 30, 60]; // Backoff delays in seconds
	
	protected string $imageId;
	protected string $prompt;
	protected int $conversationId;
	protected int $userId;
	
	/**
	 * Create a new job instance.
	 */
	public function __construct(string $imageId, string $prompt, int $conversationId, int $userId)
	{
		$this->imageId = $imageId;
		$this->prompt = $prompt;
		$this->conversationId = $conversationId;
		$this->userId = $userId;
	}
	
	/**
	 * Execute the job with enhanced retry logic for empty responses.
	 */
	public function handle(GoogleAiService $googleAi): void
	{
		$attempt = $this->attempts();
		
		try {
			Log::info("Starting image generation job", [
				'image_id'        => $this->imageId,
				'conversation_id' => $this->conversationId,
				'user_id'         => $this->userId,
				'attempt'         => $attempt,
				'max_tries'       => $this->tries,
				'model'           => config('google.image_model'),
				'prompt'          => substr($this->prompt, 0, 100) . (strlen($this->prompt) > 100 ? '...' : '')
			]);
			
			// Progressive delay based on attempt number to avoid rate limiting
			$baseDelay = 3 + ($attempt - 1) * 5; // 3s, 8s, 13s, 18s - more aggressive delays
			
			Log::info("Image generation job starting with delay", [
				'image_id' => $this->imageId,
				'attempt' => $attempt,
				'delay_seconds' => $baseDelay
			]);
			
			sleep($baseDelay);
			
			// Generate image using Google AI with retry-aware logic
			$result = $this->generateImageWithRetry($googleAi, $attempt);
			
			if ($result['success']) {
				// Update conversation with generated image info
				$conversation = Conversation::find($this->conversationId);
				if ($conversation) {
					$generatedImages = $conversation->ai_responses['generated_images'] ?? [];
					$generatedImages[$this->imageId] = [
						'prompt'       => $this->prompt,
						'path'         => $result['image_path'],
						'url'          => $result['image_url'],
						'generated_at' => now()->toISOString(),
						'attempt'      => $attempt
					];
					
					$conversation->updateAiResponse('generated_images', $generatedImages);
				}
				
				// Broadcast success to frontend
				$this->broadcastImageReady($result);
				
				Log::info("Image generation job completed successfully", [
					'image_id' => $this->imageId,
					'url'      => $result['image_url'],
					'attempt'  => $attempt
				]);
			} else {
				// Check if we should retry based on the error type
				if ($this->shouldRetryError($result['error'], $attempt)) {
					Log::warning("Image generation failed but will retry", [
						'image_id' => $this->imageId,
						'error'    => $result['error'],
						'attempt'  => $attempt,
						'retries_left' => $this->tries - $attempt
					]);
					
					throw new \Exception("Retryable error: " . $result['error']);
				} else {
					// Final failure - broadcast error
					$this->broadcastImageError($result['error']);
					
					Log::error("Image generation job failed permanently", [
						'image_id' => $this->imageId,
						'error'    => $result['error'],
						'attempt'  => $attempt
					]);
				}
			}
			
		} catch (\Exception $e) {
			$shouldRetry = $this->shouldRetryError($e->getMessage(), $attempt);
			
			Log::error("Image generation job exception", [
				'image_id' => $this->imageId,
				'error'    => $e->getMessage(),
				'attempt'  => $attempt,
				'should_retry' => $shouldRetry,
				'retries_left' => $this->tries - $attempt
			]);
			
			if (!$shouldRetry || $attempt >= $this->tries) {
				// Final failure - broadcast error
				$this->broadcastImageError($e->getMessage());
			}
			
			throw $e; // Let Laravel handle the retry logic
		}
	}
	
	/**
	 * Generate image with enhanced error detection for empty responses
	 */
	private function generateImageWithRetry(GoogleAiService $googleAi, int $attempt): array
	{
		$result = $googleAi->generateImage($this->prompt, $this->imageId);
		
		// Enhanced validation for empty or invalid responses
		if ($result['success']) {
			// Additional validation to catch empty images
			if (isset($result['image_path'])) {
				$fullPath = storage_path('app/public/' . $result['image_path']);
				if (!file_exists($fullPath) || filesize($fullPath) < 1024) { // Less than 1KB is suspicious
					Log::warning("Generated image file is too small or missing", [
						'image_id' => $this->imageId,
						'path' => $fullPath,
						'exists' => file_exists($fullPath),
						'size' => file_exists($fullPath) ? filesize($fullPath) : 0,
						'attempt' => $attempt
					]);
					
					return [
						'success' => false,
						'image_id' => $this->imageId,
						'error' => 'Generated image file is too small or corrupted',
						'prompt' => $this->prompt
					];
				}
			}
		}
		
		return $result;
	}
	
	/**
	 * Determine if an error should trigger a retry
	 */
	private function shouldRetryError(string $error, int $attempt): bool
	{
		// Don't retry if we've reached max attempts
		if ($attempt >= $this->tries) {
			return false;
		}
		
		// Retry for these specific error patterns
		$retryableErrors = [
			'No valid image data returned',
			'Generated image file is too small',
			'bytesBase64Encoded',
			'empty',
			'timeout',
			'connection',
			'network',
			'overloaded',
			'rate',
			'quota',
			'503',
			'502',
			'429',
			'No predictions in response',
			'predictions',
			'Failed to save image',
			'too small or corrupted'
		];
		
		foreach ($retryableErrors as $pattern) {
			if (stripos($error, $pattern) !== false) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Broadcast successful image generation to frontend
	 */
	private function broadcastImageReady(array $result): void
	{
		$channelName = "conversation.{$this->conversationId}";
		
		try {
			// For now, we'll use a simple event system
			// You can implement Laravel Broadcasting later if needed
			$eventData = [
				'type'            => 'image_ready',
				'image_id'        => $this->imageId,
				'url'             => $result['image_url'],
				'conversation_id' => $this->conversationId
			];
			
			// Store in cache for frontend polling (fallback method)
			cache()->put(
				"image_event_{$this->conversationId}_{$this->imageId}",
				$eventData,
				300 // 5 minutes
			);
			
			Log::info("Image ready event cached", $eventData);
			
		} catch (\Exception $e) {
			Log::error("Failed to broadcast image ready event", [
				'error'    => $e->getMessage(),
				'image_id' => $this->imageId
			]);
		}
	}
	
	/**
	 * Broadcast image generation error to frontend
	 */
	private function broadcastImageError(string $error): void
	{
		$eventData = [
			'type'            => 'image_error',
			'image_id'        => $this->imageId,
			'error'           => $error,
			'conversation_id' => $this->conversationId
		];
		
		// Store in cache for frontend polling
		cache()->put(
			"image_event_{$this->conversationId}_{$this->imageId}",
			$eventData,
			300
		);
		
		Log::info("Image error event cached", $eventData);
	}
	
	/**
	 * Handle job failure
	 */
	public function failed(\Throwable $exception): void
	{
		Log::error("Image generation job permanently failed", [
			'image_id'        => $this->imageId,
			'conversation_id' => $this->conversationId,
			'error'           => $exception->getMessage(),
			'attempts_made'   => $this->attempts()
		]);
		
		// Update conversation with failed image info
		$conversation = Conversation::find($this->conversationId);
		if ($conversation) {
			$failedImages = $conversation->ai_responses['failed_images'] ?? [];
			$failedImages[$this->imageId] = [
				'prompt' => $this->prompt,
				'error' => $exception->getMessage(),
				'failed_at' => now()->toISOString(),
				'attempts_made' => $this->attempts()
			];
			
			$conversation->updateAiResponse('failed_images', $failedImages);
		}
		
		$this->broadcastImageError("Image generation failed after {$this->tries} attempts: " . $exception->getMessage());
	}
}
