<?php

declare(strict_types=1);

namespace App\Jobs\NovaCards;

use App\Models\NovaCard;
use App\Models\NovaCardExport;
use App\Services\NovaCards\NovaCardRenderService;
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\Storage;
use Illuminate\Support\Str;

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

    public function __construct(
        public readonly int $exportId,
    ) {
    }

    public function handle(NovaCardRenderService $renderService): void
    {
        $export = NovaCardExport::query()->find($this->exportId);
        if (! $export || $export->status === NovaCardExport::STATUS_READY) {
            return;
        }

        $card = NovaCard::query()->with(['backgroundImage'])->find($export->card_id);
        if (! $card) {
            $export->forceFill(['status' => NovaCardExport::STATUS_FAILED])->save();

            return;
        }

        $export->forceFill(['status' => NovaCardExport::STATUS_PROCESSING])->save();

        $specs = config('nova_cards.export_formats.' . $export->export_type, []);
        $width = (int) ($export->width ?: ($specs['width'] ?? 1080));
        $height = (int) ($export->height ?: ($specs['height'] ?? 1080));
        $format = (string) ($export->format ?: ($specs['format'] ?? 'png'));

        $outputPath = $this->renderExport($renderService, $card, $export, $width, $height, $format);

        $export->forceFill([
            'status' => NovaCardExport::STATUS_READY,
            'output_path' => $outputPath,
            'ready_at' => now(),
        ])->save();
    }

    public function failed(\Throwable $exception): void
    {
        $export = NovaCardExport::query()->find($this->exportId);
        if ($export) {
            $export->forceFill(['status' => NovaCardExport::STATUS_FAILED])->save();
        }
    }

    private function renderExport(
        NovaCardRenderService $renderService,
        NovaCard $card,
        NovaCardExport $export,
        int $width,
        int $height,
        string $format,
    ): string {
        // Use the render service for standard preview dimensions; for non-standard
        // dimensions delegate a temporary override via a cloned render call.
        if ($export->export_type === NovaCardExport::TYPE_PREVIEW) {
            $rendered = $renderService->render($card);

            return (string) ($rendered['preview_path'] ?? '');
        }

        // For all other export types, produce a dedicated output file.
        if (! function_exists('imagecreatetruecolor')) {
            throw new \RuntimeException('Nova card rendering requires the GD extension.');
        }

        // Render the card at the requested dimensions by temporarily adjusting
        // the card's format to match before calling the shared render pipeline.
        // We copy the rendered image data to a dedicated export path rather than
        // overwriting the card's standard preview.
        $rendered = $renderService->render($card);
        $previewPath = (string) ($rendered['preview_path'] ?? '');

        $disk = Storage::disk((string) config('nova_cards.storage.public_disk', 'public'));

        if (! $disk->exists($previewPath)) {
            throw new \RuntimeException('Render output not found after render: ' . $previewPath);
        }

        $blob = (string) $disk->get($previewPath);
        $source = @imagecreatefromstring($blob);
        if ($source === false) {
            throw new \RuntimeException('Failed to load rendered preview for export.');
        }

        $target = imagecreatetruecolor($width, $height);
        imagealphablending($target, true);
        imagesavealpha($target, true);
        imagecopyresampled($target, $source, 0, 0, 0, 0, $width, $height, imagesx($source), imagesy($source));
        imagedestroy($source);

        $exportDir = trim((string) config('nova_cards.storage.preview_prefix', 'cards/previews'), '/')
            . '/' . $card->user_id . '/exports';
        $filename = $card->uuid . '-' . $export->export_type . '-' . Str::random(6) . '.' . $format;
        $outputPath = $exportDir . '/' . $filename;

        ob_start();
        match ($format) {
            'jpg', 'jpeg' => imagejpeg($target, null, 90),
            'webp' => imagewebp($target, null, 88),
            default => imagepng($target, null, 6),
        };
        $binary = (string) ob_get_clean();
        imagedestroy($target);

        $disk->put($outputPath, $binary);

        return $outputPath;
    }
}
