<?php

namespace Tests\Unit\Services;

use App\DTOs\CreativeBrief;
use App\Services\AI\LaravelAiImageGenerator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Laravel\Ai\Image;
use Laravel\Ai\Prompts\ImagePrompt;
use Laravel\Ai\Responses\Data\GeneratedImage;
use Laravel\Ai\Responses\Data\Meta;
use Laravel\Ai\Responses\Data\Usage;
use Laravel\Ai\Responses\ImageResponse;
use Tests\TestCase;

class LaravelAiImageGeneratorTest extends TestCase
{
    public function test_custom_sizes_use_openai_and_fit_without_cropping(): void
    {
        Storage::fake('public');
        config(['ai.default_for_images' => 'gemini']);
        $requests = [];
        $source = imagecreatetruecolor(1024, 1024);
        $red = imagecolorallocate($source, 255, 0, 0);
        $blue = imagecolorallocate($source, 0, 0, 255);
        imagefill($source, 0, 0, $red);
        imagesetpixel($source, 0, 0, $blue);
        imagesetpixel($source, 1023, 0, $blue);
        imagesetpixel($source, 0, 1023, $blue);
        imagesetpixel($source, 1023, 1023, $blue);
        ob_start();
        imagepng($source);
        $sourceBytes = ob_get_clean();
        imagedestroy($source);

        Image::fake(function (ImagePrompt $prompt) use (&$requests, $sourceBytes): string {
            $requests[] = [
                'provider' => $prompt->provider->name(),
                'size' => $prompt->size,
                'prompt' => $prompt->prompt,
            ];

            return base64_encode($sourceBytes);
        });

        $brief = new CreativeBrief('Rebates', ['Save on every purchase'], ['Coupons'], [], 'Clear', 'Start saving');
        $cases = [
            '500x600' => '1024x1232',
            '600x600' => '1024x1024',
            '600x1200' => '608x1200',
            '1200x628' => '1200x624',
            '1200x1200' => '1200x1200',
            '1536x864' => '1536x864',
            '150x150' => '1024x1024',
            '80x8000' => '1024x3072',
        ];
        $paths = [];

        foreach ($cases as $customSize => $nativeSize) {
            $result = (new LaravelAiImageGenerator)->generate($brief, $customSize);
            $paths[$customSize] = $result->path;
            [$expectedWidth, $expectedHeight] = array_map('intval', explode('x', $customSize));
            $dimensions = getimagesizefromstring(Storage::disk('public')->get($result->path));

            $this->assertSame([$expectedWidth, $expectedHeight], [$dimensions[0], $dimensions[1]]);
        }

        $output = imagecreatefromstring(Storage::disk('public')->get($paths['500x600']));
        $topCenter = imagecolorat($output, 250, 0);
        $imageCenter = imagecolorat($output, 250, 300);
        imagedestroy($output);

        $this->assertSame(array_values($cases), array_column($requests, 'size'));
        $this->assertSame(array_fill(0, count($cases), 'openai'), array_column($requests, 'provider'));
        $this->assertStringContainsString('finished, conversion-focused paid-social ad creative', $requests[0]['prompt']);
        $this->assertStringContainsString('Final canvas: 500 by 600 pixels', $requests[0]['prompt']);
        $this->assertStringContainsString('rendered exactly: "Start saving"', $requests[0]['prompt']);
        $this->assertSame([0, 0, 255], [($topCenter >> 16) & 0xFF, ($topCenter >> 8) & 0xFF, $topCenter & 0xFF]);
        $this->assertSame([255, 0, 0], [($imageCenter >> 16) & 0xFF, ($imageCenter >> 8) & 0xFF, $imageCenter & 0xFF]);
    }

    public function test_each_preset_saves_its_advertised_size_with_openai(): void
    {
        Storage::fake('public');
        config(['ai.default_for_images' => 'openai']);
        $requests = [];

        Image::fake(function (ImagePrompt $prompt) use (&$requests): string {
            $requests[$prompt->prompt] = [$prompt->provider->name(), $prompt->model, $prompt->size];
            [$width, $height] = array_map('intval', explode('x', $prompt->size));
            $source = imagecreatetruecolor($width, $height);
            $red = imagecolorallocate($source, 255, 0, 0);
            $blue = imagecolorallocate($source, 0, 0, 255);
            imagefill($source, 0, 0, $red);
            imagesetpixel($source, 0, 0, $blue);
            imagesetpixel($source, $width - 1, 0, $blue);
            imagesetpixel($source, 0, $height - 1, $blue);
            imagesetpixel($source, $width - 1, $height - 1, $blue);
            ob_start();
            imagepng($source);
            $sourceBytes = ob_get_clean();
            imagedestroy($source);

            return base64_encode($sourceBytes);
        });

        $brief = new CreativeBrief('Product', ['Benefit'], [], [], 'Message', 'Click');
        $sizes = [
            '1:1' => [1200, 1200, '1200x1200'],
            '9:16' => [900, 1600, '896x1600'],
            '16:9' => [1600, 900, '1600x896'],
            '4:5' => [960, 1200, '960x1200'],
            '1.91:1' => [1200, 628, '1200x624'],
        ];

        foreach ($sizes as $ratio => [$width, $height, $requestSize]) {
            $result = (new LaravelAiImageGenerator)->generate($brief, $ratio);
            $dimensions = getimagesizefromstring(Storage::disk('public')->get($result->path));

            $this->assertSame([$width, $height], [$dimensions[0], $dimensions[1]]);
            $this->assertSame(['openai', 'gpt-image-2.5-sunburst', $requestSize], $requests[$result->prompt]);

            if ($ratio === '1.91:1') {
                $savedImage = imagecreatefromstring(Storage::disk('public')->get($result->path));
                $topCenter = imagecolorat($savedImage, 600, 0);
                imagedestroy($savedImage);

                $this->assertSame([255, 0, 0], [($topCenter >> 16) & 0xFF, ($topCenter >> 8) & 0xFF, $topCenter & 0xFF]);
            }
        }
    }

    public function test_gemini_presets_keep_the_requested_aspect_ratios(): void
    {
        Storage::fake('public');
        config(['ai.default_for_images' => 'gemini']);
        $requests = [];
        $source = imagecreatetruecolor(64, 64);
        ob_start();
        imagepng($source);
        $sourceBytes = ob_get_clean();
        imagedestroy($source);

        Image::fake(function (ImagePrompt $prompt) use (&$requests, $sourceBytes): string {
            $requests[] = [$prompt->provider->name(), $prompt->size];

            return base64_encode($sourceBytes);
        });

        $brief = new CreativeBrief('Product', ['Benefit'], [], [], 'Message', 'Click');

        foreach (['1:1' => [1200, 1200], '9:16' => [900, 1600], '16:9' => [1600, 900], '4:5' => [960, 1200]] as $ratio => $expectedDimensions) {
            $result = (new LaravelAiImageGenerator)->generate($brief, $ratio);
            $dimensions = getimagesizefromstring(Storage::disk('public')->get($result->path));

            $this->assertSame($expectedDimensions, [$dimensions[0], $dimensions[1]]);
            $this->assertSame('gemini', $result->provider);
        }

        $this->assertSame([
            ['gemini', '1:1'],
            ['gemini', '9:16'],
            ['gemini', '16:9'],
            ['gemini', '4:5'],
        ], $requests);
    }

    public function test_resized_gemini_jpeg_keeps_its_file_format(): void
    {
        Storage::fake('public');
        config(['ai.default_for_images' => 'gemini']);
        $source = imagecreatetruecolor(64, 64);
        ob_start();
        imagejpeg($source, null, 90);
        $sourceBytes = ob_get_clean();
        imagedestroy($source);
        Image::fake([new ImageResponse(
            new Collection([new GeneratedImage(base64_encode($sourceBytes), 'image/jpeg')]),
            new Usage,
            new Meta('gemini', 'fake-model'),
        )]);

        $brief = new CreativeBrief('Product', ['Benefit'], [], [], 'Message', 'Click');
        $result = (new LaravelAiImageGenerator)->generate($brief, '1:1');
        $dimensions = getimagesizefromstring(Storage::disk('public')->get($result->path));

        $this->assertStringEndsWith('.jpg', $result->path);
        $this->assertSame([1200, 1200], [$dimensions[0], $dimensions[1]]);
        $this->assertSame('image/jpeg', $dimensions['mime']);
    }

    public function test_oversized_custom_image_is_rejected_before_generation(): void
    {
        Image::fake();
        $brief = new CreativeBrief('Product', ['Benefit'], [], [], 'Message', 'Click');

        try {
            (new LaravelAiImageGenerator)->generate($brief, '8000x8000');
            $this->fail('Expected an oversized image to be rejected.');
        } catch (\InvalidArgumentException $exception) {
            $this->assertSame('Custom image dimensions exceed the supported pixel limit.', $exception->getMessage());
        }

        Image::assertNothingGenerated();
    }

    public function test_it_builds_prompt_with_all_inputs_and_realism_standards(): void
    {
        Storage::fake('public');
        $requests = [];
        $source = imagecreatetruecolor(512, 512);
        ob_start();
        imagepng($source);
        $sourceBytes = ob_get_clean();
        imagedestroy($source);

        Image::fake(function (ImagePrompt $prompt) use (&$requests, $sourceBytes): string {
            $requests[] = $prompt->prompt;

            return base64_encode($sourceBytes);
        });

        $brief = new CreativeBrief(
            productName: 'Air Zoom Running Shoe',
            benefits: ['Ultra-responsive cushioning', 'Zero foot fatigue'],
            features: ['Engineered breathable mesh', 'Carbon fiber plate', 'Reflective heel tab'],
            images: [],
            messaging: 'Built for marathon personal records.',
            cta: 'Shop Running',
            projectName: 'Nike Pro Series',
        );

        $result = (new LaravelAiImageGenerator)->generate($brief, '1:1', 'Clean Beauty');

        $prompt = $requests[0];
        $this->assertStringContainsString('Brand/Project: Nike Pro Series', $prompt);
        $this->assertStringContainsString('Product: Air Zoom Running Shoe', $prompt);
        $this->assertStringContainsString('Engineered breathable mesh; Carbon fiber plate; Reflective heel tab', $prompt);
        $this->assertStringContainsString('Ultra-responsive cushioning; Zero foot fatigue', $prompt);
        $this->assertStringContainsString('Built for marathon personal records.', $prompt);
        $this->assertStringContainsString('rendered exactly: "Shop Running"', $prompt);
        $this->assertStringContainsString('Photorealistic, 8k resolution commercial photograph', $prompt);
        $this->assertStringContainsString('authentic material textures', $prompt);
        $this->assertStringContainsString('Avoid cartoonish rendering, CGI plastic gloss', $prompt);
    }

    public function test_aspect_ratio_custom_sizes_are_normalized_to_high_resolution(): void
    {
        Storage::fake('public');
        $requests = [];
        $source = imagecreatetruecolor(1024, 1024);
        ob_start();
        imagepng($source);
        $sourceBytes = ob_get_clean();
        imagedestroy($source);

        Image::fake(function (ImagePrompt $prompt) use (&$requests, $sourceBytes): string {
            $requests[] = [
                'provider' => $prompt->provider->name(),
                'size' => $prompt->size,
                'prompt' => $prompt->prompt,
            ];

            return base64_encode($sourceBytes);
        });

        $generator = new LaravelAiImageGenerator;

        $this->assertSame([1080, 1080], $generator->normalizeCustomDimensions('16x16'));
        $this->assertSame([1920, 1080], $generator->normalizeCustomDimensions('16x9'));
        $this->assertSame([1080, 1920], $generator->normalizeCustomDimensions('9x16'));
        $this->assertSame([1080, 1350], $generator->normalizeCustomDimensions('4x5'));
        $this->assertSame([150, 150], $generator->normalizeCustomDimensions('150x150'));
        $this->assertSame([80, 8000], $generator->normalizeCustomDimensions('1x100'));

        $brief = new CreativeBrief('Product', ['Benefit'], [], [], 'Message', 'Click');
        $result = $generator->generate($brief, '16x16');

        $dimensions = getimagesizefromstring(Storage::disk('public')->get($result->path));
        $this->assertSame([1080, 1080], [$dimensions[0], $dimensions[1]]);
        $this->assertStringContainsString('Final canvas: 1080 by 1080 pixels', $requests[0]['prompt']);
    }
}
