<?php

use App\Ai\Agents\MealMaker;
use App\Enums\AiUsageStatus;
use App\Enums\AiUsageType;
use App\Enums\IngredientUnit;
use App\Enums\MealPostType;
use App\Enums\MealPostVisibility;
use App\Models\AiUsage;
use App\Models\MealPosts;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Exceptions;
use Illuminate\Support\Facades\Storage;
use Laravel\Ai\Image;

uses(RefreshDatabase::class);

it('generates an ai meal post with an image', function () {
    config()->set('filesystems.default', 's3');

    Storage::fake('s3');

    config()->set('meal_posts.ai_generation.user_name', 'Chef IA');
    config()->set('meal_posts.ai_generation.user_email', 'chef-ia@example.test');
    config()->set('meal_posts.ai_generation.image_path', 'meal-posts/ai-generated');
    config()->set('meal_posts.ai_generation.image_quality', 'medium');

    foreach (range(1, 11) as $index) {
        $mealPost = MealPosts::factory()->create([
            'eaten_at' => now()->subMinutes(11 - $index),
        ]);

        $mealPost->ingredients()->createMany([
            [
                'position' => 1,
                'ingredient' => $index === 1 ? 'Topinambour ancien' : "Ingredient recent {$index}",
                'quantity' => 120,
                'unit' => IngredientUnit::GRAM->value,
            ],
            [
                'position' => 2,
                'ingredient' => $index === 11 ? 'Asperges' : "Accompagnement recent {$index}",
                'quantity' => 80,
                'unit' => IngredientUnit::GRAM->value,
            ],
            [
                'position' => 4,
                'ingredient' => "Garniture ignoree {$index}",
                'quantity' => 10,
                'unit' => IngredientUnit::GRAM->value,
            ],
        ]);
    }

    MealMaker::fake([
        [
            'title' => 'Bowl de saumon croustillant',
            'caption' => 'Un bowl colore avec saumon, riz vinaigre et legumes croquants.',
            'type' => MealPostType::LUNCH->value,
            'calories' => 640,
            'proteins' => 42.4,
            'carbs' => 62.2,
            'fats' => 24.8,
            'ingredients' => [
                [
                    'position' => 12,
                    'ingredient' => 'Riz vinaigre',
                    'quantity' => 160,
                    'unit' => IngredientUnit::GRAM->value,
                ],
                [
                    'position' => 13,
                    'ingredient' => 'Saumon',
                    'quantity' => 130,
                    'unit' => IngredientUnit::GRAM->value,
                ],
            ],
            'image_prompt' => 'Photorealistic crispy salmon bowl with vinegared rice and crunchy vegetables',
        ],
    ]);

    Image::fake([
        base64_encode('fake-image-content'),
    ]);

    $this->artisan('meal-posts:generate-ai')
        ->assertExitCode(0);

    $mealPost = MealPosts::query()
        ->where('ai_generated', true)
        ->with('user', 'ingredients')
        ->firstOrFail();

    expect($mealPost->ai_generated)->toBeTrue()
        ->and($mealPost->title)->toBe('Bowl de saumon croustillant')
        ->and($mealPost->type)->toBe(MealPostType::LUNCH)
        ->and($mealPost->visibility)->toBe(MealPostVisibility::Public)
        ->and($mealPost->user->email)->toBe('chef-ia@example.test')
        ->and($mealPost->user->name)->toBe('Chef IA')
        ->and($mealPost->ingredients)->toHaveCount(2)
        ->and($mealPost->ingredients[0]->position)->toBe(1)
        ->and($mealPost->ingredients[0]->ingredient)->toBe('Riz vinaigre');

    expect($mealPost->image_url)->toStartWith('meal-posts/ai-generated/');
    Storage::disk('s3')->assertExists($mealPost->image_url);

    $this->assertDatabaseHas('meal_posts', [
        'id' => $mealPost->id,
        'ai_generated' => true,
        'visibility' => MealPostVisibility::Public->value,
    ]);

    MealMaker::assertPrompted(
        fn ($prompt): bool => str_contains($prompt->prompt, 'Genere un plat completement aleatoire')
            && str_contains($prompt->prompt, 'sans tenir compte du mois actuel')
            && str_contains($prompt->prompt, 'Evite les ingredients principaux deja utilises dans les 10 derniers repas')
            && str_contains($prompt->prompt, 'Ingredient recent 2')
            && str_contains($prompt->prompt, 'Asperges')
            && ! str_contains($prompt->prompt, 'Topinambour ancien')
            && ! str_contains($prompt->prompt, 'Garniture ignoree 11')
    );

    Image::assertGenerated(
        fn ($prompt): bool => $prompt->contains('crispy salmon bowl')
            && $prompt->isLandscape()
            && $prompt->quality === 'medium'
    );

    $usage = AiUsage::query()->sole();

    expect($usage->user_id)->toBe($mealPost->user_id)
        ->and($usage->type)->toBe(AiUsageType::MEAL_POST_GENERATION)
        ->and($usage->status)->toBe(AiUsageStatus::SUCCESS)
        ->and($usage->related_type)->toBe(MealPosts::class)
        ->and($usage->related_id)->toBe($mealPost->id)
        ->and($usage->input['image'])->toMatchArray([
            'size' => '3:2',
            'quality' => 'medium',
            'storage_path' => 'meal-posts/ai-generated',
        ])
        ->and($usage->output)->toMatchArray([
            'title' => 'Bowl de saumon croustillant',
            'type' => MealPostType::LUNCH->value,
            'calories' => 640,
            'image_url' => $mealPost->image_url,
            'ingredients' => [
                [
                    'position' => 1,
                    'ingredient' => 'Riz vinaigre',
                    'quantity' => 160,
                    'unit' => IngredientUnit::GRAM->value,
                ],
                [
                    'position' => 2,
                    'ingredient' => 'Saumon',
                    'quantity' => 130,
                    'unit' => IngredientUnit::GRAM->value,
                ],
            ],
        ]);
});

it('records a failed ai meal post generation attempt', function () {
    config()->set('filesystems.default', 's3');

    Storage::fake('s3');
    Exceptions::fake();

    config()->set('meal_posts.ai_generation.user_name', 'Chef IA');
    config()->set('meal_posts.ai_generation.user_email', 'chef-ia@example.test');
    config()->set('meal_posts.ai_generation.image_quality', 'medium');

    MealMaker::fake([
        [
            'title' => 'Bowl impossible',
            'caption' => 'Un bowl qui ne sera pas publie.',
            'type' => MealPostType::LUNCH->value,
            'calories' => 640,
            'proteins' => 42.4,
            'carbs' => 62.2,
            'fats' => 24.8,
            'ingredients' => [],
            'image_prompt' => 'Photorealistic impossible bowl',
        ],
    ]);

    Image::fake(
        fn () => throw new \RuntimeException('Image provider unavailable')
    );

    $this->artisan('meal-posts:generate-ai')
        ->assertExitCode(1);

    Exceptions::assertReported(\RuntimeException::class);

    expect(MealPosts::query()->where('ai_generated', true)->count())->toBe(0);

    $usage = AiUsage::query()->sole();

    expect($usage->user->email)->toBe('chef-ia@example.test')
        ->and($usage->type)->toBe(AiUsageType::MEAL_POST_GENERATION)
        ->and($usage->status)->toBe(AiUsageStatus::FAILED)
        ->and($usage->input['image_prompt'])->toContain('impossible bowl')
        ->and($usage->output)->toBeNull()
        ->and($usage->error_message)->toContain('Image provider unavailable');
});
