<?php

namespace App\Services;

use App\Exceptions\ProductRatingException;
use App\Models\Product;
use App\Models\ProductRating;
use App\Models\User;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;

trait ProductRatingService
{
    /**
     * Store new record
     *
     * @param array $data
     * @return Builder|Model
     */
    public static function store(array $data): Model|Builder
    {
        return ProductRating::query()->create($data);
    }

    /**
     * Update existing record
     *
     * @param array $data
     * @return mixed
     */
    public function updateService(array $data): mixed
    {
        return tap($this)->update($data);
    }

    /**
     * Compute average of product rating
     *
     * @param Product $product
     * @return mixed
     */
    public static function computeAverageRating(Product $product): mixed
    {
        $result =
            ProductRating::query()
                ->where('product_id', $product->{'id'})
                ->avg('rating');

        return $result ?? 0.0;
    }

    /**
     * Get all product ratings
     *
     * @param Product $product
     * @param int|null $page
     * @return LengthAwarePaginator|Builder[]|Collection
     */
    public static function allProductRatings(Product $product, int $page = null): Collection|LengthAwarePaginator|array
    {
        $queryBuilder =
            ProductRating::query()
                ->with('productRatingAttachments', 'user.userDetail', 'user.business')
                ->where('product_id', $product->{'id'})
                ->orderByDesc('updated_at');

        if ($page !== null) {
            $paginator = $queryBuilder->paginate(
                perPage: config('torryme.constants.items_per_page'),
                page: $page
            );

            return self::computeDetailFromPaginator($paginator);
        }

        return $queryBuilder->get();
    }

    /**
     * Compute details from paginator
     *
     * @param LengthAwarePaginator $paginator
     * @return array
     */
    public static function computeDetailFromPaginator(LengthAwarePaginator $paginator): array
    {
        return default_paginator_format(
            $paginator->lastPage(),
            $paginator->total(),
            $paginator->currentPage(),
            "ratings",
            $paginator->items(),
        );
    }

    /**
     * Total product ratings
     *
     * @param Product $product
     * @return int
     */
    public static function totalProductRating(Product $product): int
    {
        return ProductRating::query()->where('product_id', $product->{'id'})->count();
    }

    /**
     *  Check reviewer is not product owner
     *
     * @param array $data
     * @return false
     * @throws ProductRatingException
     */
    public static function checkProductIsNotOwnedByCurrentReviewer(array $data): bool
    {
        /** @var User $user */
        $user = auth()->user();
        $business = $user->{'business'};

        $product = Product::findById($data["product_id"]);
        if ($business !== null && $product->{"business_id"} == $business->{"id"}) {
            throw new ProductRatingException(__('errors.can_not_review_product_you_own'));
        }

        return false;
    }

    /**
     * Check if current user has already reviewed product
     *
     * @param array $data
     * @return Model|Builder
     * @throws \Exception
     */
    public static function createOrUpdateReview(array $data): Model|Builder
    {
        try {
            //TODO Ensure product owner cannot review product

            // self::checkProductIsNotOwnedByCurrentReviewer($data);

            /** @var User $user */
            $user = auth()->user();

            $userReview = self::lastUserRating($user, $data["product_id"]);

            // User rating found, update rating
            if ($userReview != null) {
                $reviewResults = $userReview->updateService([
                    "rating" => $data["rating"],
                    "review" => $data["review"],
                ]);
            } else {
                // User rating not found, create rating
                $reviewResults = self::store([
                    "rating" => $data["rating"],
                    "review" => $data["review"],
                    "product_id" => $data["product_id"],
                    "user_id" => $user->{"id"},
                ]);
            }

            $result = $reviewResults->refresh();
        } catch (\Exception $exception) {
            log_debug(exception: $exception, prefix: 'ProductRatingService::createOrUpdateReview');
            throw  $exception;
        }

        return $result;
    }

    /**
     * Last user rate
     *
     * @param User $user
     * @param int $productId
     * @return Builder|Model|null
     */
    public static function lastUserRating(User $user, int $productId): Model|Builder|null
    {
        return
            ProductRating::query()
                ->where("product_id", $productId)
                ->where("user_id", $user->{"id"})
                ->orderByDesc('updated_at')
                ->first();
    }

    /**
     * Check if user already rated product
     *
     * @param User $user
     * @param int $productId
     * @return bool
     */
    public static function alreadyRated(User $user, int $productId): bool
    {
        return self::lastUserRating($user, $productId) !== null;
    }


}
