<?php

use App\Models\MealPosts;
use App\Models\PostReviews;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Laravel\Sanctum\Sanctum;

uses(RefreshDatabase::class);

it('creates one review per user for a meal post', function () {
    $user = User::factory()->create();
    $mealPost = MealPosts::factory()->create();

    Sanctum::actingAs($user);

    $this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
        'rating' => 5,
        'comment' => 'Très bon repas.',
    ])
        ->assertCreated()
        ->assertJsonPath('data.rating', 5)
        ->assertJsonPath('data.comment', 'Très bon repas.')
        ->assertJsonPath('data.meal_post_id', $mealPost->id)
        ->assertJsonPath('data.user_id', $user->id);

    $this->assertDatabaseHas('post_reviews', [
        'meal_post_id' => $mealPost->id,
        'user_id' => $user->id,
        'rating' => 5,
        'comment' => 'Très bon repas.',
    ]);

    $this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
        'rating' => 4,
    ])
        ->assertConflict()
        ->assertJsonPath('message', __('api.post_reviews.already_exists'));

    expect(PostReviews::query()->count())->toBe(1);
});

it('sends a push notification to the meal owner when another user comments on their meal', function () {
    config()->set('app.mobile_scheme', 'dailymeal');

    Http::fake([
        'https://exp.host/*' => Http::response([
            'data' => [
                ['status' => 'ok', 'id' => 'ticket-one'],
            ],
        ]),
    ]);

    $owner = User::factory()->create();
    $owner->deviceTokens()->create([
        'expo_push_token' => 'ExponentPushToken[owner]',
        'platform' => 'ios',
    ]);
    $reviewer = User::factory()->create(['name' => 'Alex']);
    $mealPost = MealPosts::factory()
        ->for($owner, 'user')
        ->create(['title' => 'Recovery bowl']);

    Sanctum::actingAs($reviewer);

    $this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
        'rating' => 5,
        'comment' => 'Très bon repas.',
    ])->assertCreated();

    Http::assertSent(fn (Request $request) => $request->url() === 'https://exp.host/--/api/v2/push/send'
        && $request->data() === [
            [
                'to' => 'ExponentPushToken[owner]',
                'title' => 'New comment',
                'body' => 'Alex commented on your meal "Recovery bowl".',
                'sound' => 'default',
                'channelId' => 'default',
                'data' => [
                    'url' => "dailymeal://meals/{$mealPost->id}",
                ],
            ],
        ]);
});

it('does not send a push notification for rating-only reviews', function () {
    Http::fake();

    $owner = User::factory()->create();
    $owner->deviceTokens()->create([
        'expo_push_token' => 'ExponentPushToken[owner]',
        'platform' => 'ios',
    ]);
    $reviewer = User::factory()->create();
    $mealPost = MealPosts::factory()->for($owner, 'user')->create();

    Sanctum::actingAs($reviewer);

    $this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
        'rating' => 5,
    ])->assertCreated();

    Http::assertNothingSent();
});

it('does not notify the owner when they comment on their own meal', function () {
    Http::fake();

    $owner = User::factory()->create();
    $owner->deviceTokens()->create([
        'expo_push_token' => 'ExponentPushToken[owner]',
        'platform' => 'ios',
    ]);
    $mealPost = MealPosts::factory()->for($owner, 'user')->create();

    Sanctum::actingAs($owner);

    $this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
        'rating' => 5,
        'comment' => 'Note personnelle.',
    ])->assertCreated();

    Http::assertNothingSent();
});

it('lists reviews for a single meal post', function () {
    $user = User::factory()->create();
    $mealPost = MealPosts::factory()->create();
    $otherMealPost = MealPosts::factory()->create();
    $firstReview = PostReviews::query()->create([
        'meal_post_id' => $mealPost->id,
        'user_id' => User::factory()->create()->id,
        'rating' => 4,
        'comment' => 'Solide.',
    ]);
    $secondReview = PostReviews::query()->create([
        'meal_post_id' => $mealPost->id,
        'user_id' => User::factory()->create()->id,
        'rating' => 5,
        'comment' => null,
    ]);
    $otherReview = PostReviews::query()->create([
        'meal_post_id' => $otherMealPost->id,
        'user_id' => User::factory()->create()->id,
        'rating' => 1,
        'comment' => 'Pas celui-ci.',
    ]);

    Sanctum::actingAs($user);

    $this->getJson("/api/meal-posts/{$mealPost->id}/reviews")
        ->assertOk()
        ->assertJsonCount(2, 'data')
        ->assertJsonFragment(['id' => $firstReview->id])
        ->assertJsonFragment(['id' => $secondReview->id])
        ->assertJsonMissing(['id' => $otherReview->id]);
});

it('shows the authenticated users review for a meal post', function () {
    $user = User::factory()->create();
    $mealPost = MealPosts::factory()->create();
    $review = PostReviews::query()->create([
        'meal_post_id' => $mealPost->id,
        'user_id' => $user->id,
        'rating' => 4,
        'comment' => 'Mon avis.',
    ]);
    PostReviews::query()->create([
        'meal_post_id' => $mealPost->id,
        'user_id' => User::factory()->create()->id,
        'rating' => 5,
        'comment' => 'Un autre avis.',
    ]);

    Sanctum::actingAs($user);

    $this->getJson("/api/meal-posts/{$mealPost->id}/my-review")
        ->assertOk()
        ->assertJsonPath('data.id', $review->id)
        ->assertJsonPath('data.user_id', $user->id);
});

it('returns null when the authenticated user has not reviewed a meal post', function () {
    $user = User::factory()->create();
    $mealPost = MealPosts::factory()->create();

    Sanctum::actingAs($user);

    $this->getJson("/api/meal-posts/{$mealPost->id}/my-review")
        ->assertOk()
        ->assertJsonPath('data', null);
});

it('lets the review author update and delete their review', function () {
    $user = User::factory()->create();
    $review = PostReviews::query()->create([
        'meal_post_id' => MealPosts::factory()->create()->id,
        'user_id' => $user->id,
        'rating' => 3,
        'comment' => 'Initial.',
    ]);

    Sanctum::actingAs($user);

    $this->patchJson("/api/post-reviews/{$review->id}", [
        'rating' => 4,
        'comment' => null,
    ])
        ->assertOk()
        ->assertJsonPath('data.rating', 4)
        ->assertJsonPath('data.comment', null);

    $this->assertDatabaseHas('post_reviews', [
        'id' => $review->id,
        'rating' => 4,
        'comment' => null,
    ]);

    $this->deleteJson("/api/post-reviews/{$review->id}")
        ->assertNoContent();

    $this->assertDatabaseMissing('post_reviews', [
        'id' => $review->id,
    ]);
});

it('does not let another user mutate a review', function () {
    $review = PostReviews::query()->create([
        'meal_post_id' => MealPosts::factory()->create()->id,
        'user_id' => User::factory()->create()->id,
        'rating' => 3,
        'comment' => 'Initial.',
    ]);

    Sanctum::actingAs(User::factory()->create());

    $this->patchJson("/api/post-reviews/{$review->id}", [
        'rating' => 4,
    ])->assertNotFound();

    $this->deleteJson("/api/post-reviews/{$review->id}")
        ->assertNotFound();

    $this->assertDatabaseHas('post_reviews', [
        'id' => $review->id,
        'rating' => 3,
    ]);
});

it('requires authentication to create a review', function () {
    $mealPost = MealPosts::factory()->create();

    $this->postJson("/api/meal-posts/{$mealPost->id}/reviews", [
        'rating' => 5,
    ])->assertUnauthorized();
});
