<?php

namespace App\Repositories\General;

use Exception;
use Throwable;
use App\Models\Post;
use App\Models\Group;
use App\Enums\PostType;
use App\Enums\SettingKey;
use App\Enums\DeletedType;
use Illuminate\Http\Request;
use App\Services\ImageService;
use App\Enums\NotificationType;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class PostRepository
{
    protected $imageService;
    protected $notificationRepository;

    public function __construct(ImageService $imageService, NotificationRepository $notificationRepository)
    {
        $this->imageService = $imageService;
        $this->notificationRepository = $notificationRepository;
    }

    public function getAll()
    {
        $posts = Post::with([
            'images',
            'user.image',
            'comments.user.image',
            'comments.likes.image',
            'likes.image',
            'group.images',
        ])
            ->filter(request([
                'search',
            ]))
            ->available()
            ->latest('updated_at')
            ->paginate(12)
            ->withQueryString();

        return compact('posts');
    }

    public function show($id)
    {
        $post = Post::with([
            'images',
            'user.image',
            'comments.user.image',
            'comments.likes.image',
            'likes.image',
            'group.images',
        ])
            ->available()
            ->findOrFail($id);

        if ($post->type == PostType::GROUP->value && $post->is_accepted == false) {
            $group = Group::findOrFail($post->origin_id);
            abort_unless($group->user_id == auth()->id(), 403, 'Unauthorized action.');
        }

        return compact('post');
    }

    public function save(Request $request)
    {
        $loginUser = auth()->user();
        $group = null;
        $originId = null;
        switch ($request->type) {
            case PostType::GROUP->value:
                $group = Group::findOrFail($request->origin_id);
                abort_unless($group->acceptedMembers()->find($loginUser->id)?->exists(), 403, 'Unauthorized action.');
                $originId = $request->origin_id;
                break;
            case PostType::SHARE_PUBLIC->value:
            case PostType::SHARE_FRIENDS->value:
            case PostType::SHARE_PRIVATE->value:
                $originId = $request->origin_id;
                break;
        }

        try {
            DB::beginTransaction();
            $post = Post::updateOrCreate(
                ['id' => $request->id],
                [
                    'user_id' => $loginUser->id,
                    'content' => $request->content,
                    'type' => $request->type,
                    'origin_id' => $originId,
                ],
            );

            if (count($request->images) > 0) {
                $oldImages = $post->images->pluck('id');
                if ($oldImages) {
                    $this->imageService->deleteMany($oldImages, Post::class);
                }
                $images = $this->imageService->uploadMany($request->images, Post::class, $post->id);
                $post->images()->saveMany($images);
            }

            if ($request->id == null) {
                switch ($request->type) {
                    case PostType::PUBLIC->value:
                    case PostType::FRIENDS->value:
                        $friends = $loginUser->getFriends();
                        foreach ($friends as $friend) {
                            if ((bool) SettingRepository::getValueByName(SettingKey::NOTIFICATION_FRIEND_POSTS, $friend->id)) {
                                $this->notificationRepository->add(
                                    userId: $friend->id,
                                    content: $loginUser->name . ' posted a new post.',
                                    imageUrl: $loginUser->image?->url,
                                    type: NotificationType::FRIEND->value,
                                    redirectLink: route('post.show', $post->id)
                                );
                            }
                        }
                        break;
                    case PostType::SHARE_PUBLIC->value:
                    case PostType::SHARE_FRIENDS->value:
                        $friends = $loginUser->getFriends();
                        foreach ($friends as $friend) {
                            if ((bool) SettingRepository::getValueByName(SettingKey::NOTIFICATION_FRIEND_POSTS, $friend->id)) {
                                $this->notificationRepository->add(
                                    userId: $friend->id,
                                    content: $loginUser->name . ' shared a post.',
                                    imageUrl: $loginUser->image?->url,
                                    type: NotificationType::FRIEND->value,
                                    redirectLink: route('post.show', $post->id)
                                );
                            }
                        }
                        break;
                    case PostType::GROUP->value:
                        $this->notificationRepository->add(
                            userId: $group->user_id,
                            content: $loginUser->name . " requested to post on {$group->name}.",
                            imageUrl: $group->images()->profile()->first()->url,
                            type: NotificationType::GROUP->value,
                            redirectLink: route('post.show', $post->id)
                        );
                        break;
                }
            }

            DB::commit();
        } catch (Throwable $e) {
            DB::rollBack();

            Log::error(__CLASS__ . '::' . __FUNCTION__ . '[line: ' . __LINE__ . ']Message: ' . $e->getMessage());

            throw new Exception('Post saved failed.');
        }
    }

    public function destroy(Post $post)
    {
        abort_unless($post->user_id == auth()->id(), 403, 'Unauthorized action.');

        try {
            DB::beginTransaction();

            $post->update([
                'deleted_type' => DeletedType::USER,
            ]);
            $post->delete();

            DB::commit();
        } catch (Throwable $e) {
            DB::rollBack();

            Log::error(__CLASS__ . '::' . __FUNCTION__ . '[line: ' . __LINE__ . ']Message: ' . $e->getMessage());

            throw new Exception('Post deleted failed.');
        }
    }

    public function restore($id)
    {
        $post = Post::onlyTrashed()->where('type', '<>', PostType::GROUP)->owned()->findOrFail($id);

        abort_unless($post->user_id == auth()->id(), 403, 'Unauthorized action.');

        try {
            DB::beginTransaction();

            $post->update([
                'deleted_type' => null,
            ]);
            $post->restore();

            DB::commit();
        } catch (Throwable $e) {
            DB::rollBack();

            Log::error(__CLASS__ . '::' . __FUNCTION__ . '[line: ' . __LINE__ . ']Message: ' . $e->getMessage());

            throw new Exception('Post restored failed.');
        }
    }

    public function like(Post $post)
    {
        $loginUser = auth()->user();
        $user = $post->likes()->find($loginUser->id);

        try {
            DB::beginTransaction();

            if (!$user) {
                if ((bool) SettingRepository::getValueByName(SettingKey::NOTIFICATION_MY_POST_COMMENT, $post->user_id)) {
                    $this->notificationRepository->add(
                        userId: $post->user_id,
                        content: $loginUser->name . ' liked your post.',
                        imageUrl: $loginUser->image?->url,
                        type: NotificationType::POST->value,
                        redirectLink: route('post.show', $post->id)
                    );
                }
            }
            $post->likes()->toggle([$loginUser->id]);

            DB::commit();
        } catch (Throwable $e) {
            DB::rollBack();

            Log::error(__CLASS__ . '::' . __FUNCTION__ . '[line: ' . __LINE__ . ']Message: ' . $e->getMessage());

            throw new Exception('Post liked failed.');
        }

        return $this->show($post->id);
    }
}
