<?php

namespace App\Services;

use App\Models\Post;
use App\Models\Save;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;

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

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

    /**
     * Save/UnSave post
     *
     * @param array $data
     * @return Builder|Model|null
     */
    public static function savePost(array $data): Model|Builder|null
    {
        DB::beginTransaction();

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

            /** @var Post $post */
            $post = Post::findById($data["post_id"]);

            $favouriteCategoryId = $data["favourite_category_id"] ?? null;
            $alreadySaved = self::alreadySavedPost(
                $user,
                $data["post_id"],
                $favouriteCategoryId,
            );

            if ($alreadySaved) {
                self::unSavePost($data["post_id"], $favouriteCategoryId);
                $post->subtractPostSave();
            } else {
                $saveAction = Save::store([
                    "post_id" => $data["post_id"],
                    "user_id" => $user->{"id"},
                    "favourite_category_id" => $favouriteCategoryId,
                ]);

                $saveAction->{"currentPost"}->addPostSave();
            }

            DB::commit();
            $result = $post->refresh();
        } catch (\Exception $exception) {
            log_debug(exception: $exception, prefix: 'SaveService::savePost');
            DB::rollBack();

            $result = null;
        }

        return $result;
    }

    /**
     * Check if user has already saved the post
     *
     * @param User $user
     * @param int $postId
     * @param ?int $favoriteCategoryId
     * @return bool
     */
    public static function alreadySavedPost(User $user, int $postId, int $favoriteCategoryId = null): bool
    {
        //Query table
        return
            Save::query()
                ->where("user_id", $user->{"id"})
                ->where("post_id", $postId)
                ->when($favoriteCategoryId === null, function ($builder) {
                    $builder->whereNull("favourite_category_id");
                })
                ->when($favoriteCategoryId !== null, function ($builder) use($favoriteCategoryId) {
                    $builder->where("favourite_category_id", $favoriteCategoryId);
                })
                ->exists();
    }

    /**
     * Get save post occurrence
     *
     * @param User $user
     * @param int $postId
     * @param bool $withFavorite
     * @return int
     */
    public static function totalSavePost(User $user, int $postId, bool $withFavorite = true): int
    {
        $favoriteCount =
            Save::query()
                ->where("user_id", $user->{"id"})
                ->where("post_id", $postId)
                ->whereNotNull("favourite_category_id")
                ->distinct()
                ->count(['post_id', 'favourite_category_id']);

        $defaultCount =
            Save::query()
                ->where("user_id", $user->{"id"})
                ->where("post_id", $postId)
                ->whereNull("favourite_category_id")
                ->distinct()
                ->count(['post_id']);

        return $withFavorite ? $defaultCount + $favoriteCount : $defaultCount;
    }

    /**
     * Un Saved post
     *
     * @param int $postId
     * @param ?int $favoriteCategoryId
     * @return mixed
     */
    public static function unSavePost(int $postId, int $favoriteCategoryId = null): mixed
    {
        /** @var User $user */
        $user = auth()->user();

        return
            Save::query()
                ->where("user_id", $user->{"id"})
                ->where("post_id", $postId)
                ->when($favoriteCategoryId === null, function ($builder) {
                    $builder->whereNull("favourite_category_id");
                })
                ->when($favoriteCategoryId !== null, function ($builder) use($favoriteCategoryId) {
                    $builder->where("favourite_category_id", $favoriteCategoryId);
                })
                ->delete();
    }

    /**
     * Get request user saved post
     *
     * @param User $user
     * @param int|null $pageNumber
     * @return array
     */
    public static function userSavedPosts(User $user, int $pageNumber = null): array
    {
        $queryBuilder = Save::query()->with('currentPost')->where("user_id", $user->{"id"})->orderByDesc("created_at");
        $savedPostPaginator = $queryBuilder->paginate(
            perPage: config("torryme.constants.items_per_page"),
            page: $pageNumber
        );

        $savedPostArray = array();
        foreach ($savedPostPaginator as $savedPost) {
            $post = $savedPost->{"currentPost"};

            $savedPostArray[] = $post->commonSerialization();
        }

        return default_paginator_format(
            $savedPostPaginator->lastPage(),
            $savedPostPaginator->total(),
            $savedPostPaginator->currentPage(),
            "posts",
            $savedPostArray,
        );
    }
}
