<?php

namespace App\Http\Controllers;

use App\Http\Requests\PostReviewsRequest;
use App\Http\Resources\PostReviewsResource;
use App\Models\MealPosts;
use App\Models\PostReviews;
use App\Notifications\MealPostCommentedNotification;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Response;

class PostReviewsController extends Controller
{
    public function index(Request $request, MealPosts $mealPost): AnonymousResourceCollection
    {
        $this->abortIfUnavailable($mealPost);

        $perPage = max(1, min($request->integer('per_page', 15), 100));

        $reviews = $mealPost->reviews()
            ->with('user:id,name,avatar_url')
            ->latest()
            ->paginate($perPage)
            ->withQueryString();

        return PostReviewsResource::collection($reviews);
    }

    public function store(PostReviewsRequest $request, MealPosts $mealPost): JsonResponse
    {
        $this->abortIfUnavailable($mealPost);

        $userId = $request->user()->getKey();

        abort_if(
            PostReviews::query()
                ->where('meal_post_id', $mealPost->getKey())
                ->where('user_id', $userId)
                ->exists(),
            409,
            __('api.post_reviews.already_exists')
        );

        $postReview = PostReviews::create([
            ...$request->validated(),
            'meal_post_id' => $mealPost->getKey(),
            'user_id' => $userId,
        ]);

        $postReview->load('user:id,name,avatar_url');
        $this->notifyMealOwner($mealPost, $postReview, $userId);

        return (new PostReviewsResource($postReview))
            ->response()
            ->setStatusCode(201);
    }

    public function mine(Request $request, MealPosts $mealPost): JsonResponse|PostReviewsResource
    {
        $this->abortIfUnavailable($mealPost);

        $postReview = $mealPost->reviews()
            ->where('user_id', $request->user()->getKey())
            ->with('user:id,name,avatar_url')
            ->first();

        if (! $postReview) {
            return response()->json(['data' => null]);
        }

        return new PostReviewsResource($postReview);
    }

    public function show(PostReviews $postReview): PostReviewsResource
    {
        $postReview->loadMissing('mealPost.user');
        $this->abortIfUnavailable($postReview->mealPost);

        return new PostReviewsResource($postReview->loadMissing(
            'mealPost.ingredients',
            'mealPost.user:id,name,avatar_url',
            'user:id,name,avatar_url',
        ));
    }

    public function update(PostReviewsRequest $request, PostReviews $postReview): PostReviewsResource
    {
        $this->abortIfNotOwner($request, $postReview);

        $postReview->update($request->validated());

        return new PostReviewsResource($postReview->refresh()->loadMissing('user:id,name,avatar_url'));
    }

    public function destroy(Request $request, PostReviews $postReview): Response
    {
        $this->abortIfNotOwner($request, $postReview);

        $postReview->delete();

        return response()->noContent();
    }

    private function abortIfNotOwner(Request $request, PostReviews $postReview): void
    {
        abort_unless($request->user(), 401);
        abort_unless($postReview->user_id === $request->user()->getKey(), 404);
    }

    private function abortIfUnavailable(MealPosts $mealPost): void
    {
        abort_if(
            $mealPost->isHidden() || $mealPost->user()->whereNotNull('suspended_at')->exists(),
            404,
        );
    }

    private function notifyMealOwner(MealPosts $mealPost, PostReviews $postReview, string $reviewerId): void
    {
        if ($mealPost->user_id === $reviewerId || blank($postReview->comment)) {
            return;
        }

        $mealPost->loadMissing('user');
        $owner = $mealPost->user;

        if (! $owner) {
            return;
        }

        $owner->notify(
            (new MealPostCommentedNotification($postReview))
                ->locale($owner->preferredLocale())
        );
    }
}
