<?php

namespace App\Repositories\General;

use Exception;
use Throwable;
use App\Models\User;
use App\Models\Friend;
use App\Enums\NotificationType;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class FriendRepository
{
    protected $notificationRepository;

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

    public function getAll()
    {
        // TODO: Collection Pagination
        $friends = auth()->user()->getFriends();

        return compact('friends');
    }

    public function add(User $user)
    {
        $loginUser = auth()->user();
        $friend = Friend::getByUsers($loginUser->id, $user->id)->first();
        abort_if($friend?->exists(), 409, 'Already requested.');

        if (!$friend) {
            try {
                DB::beginTransaction();

                Friend::create([
                    'from_user_id' => $loginUser->id,
                    'to_user_id' => $user->id,
                ]);

                $this->notificationRepository->add(
                    userId: $user->id,
                    content: $loginUser->name . ' sent you friend request.',
                    imageUrl: $loginUser->image?->url,
                    type: NotificationType::FRIEND->value,
                    redirectLink: route('user.show', $loginUser->id)
                );

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

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

                throw new Exception('Friend added failed.');
            }
        }
    }

    public function seen(User $user)
    {
        $loginUserId = auth()->id();
        $friend = Friend::getByUsers($loginUserId, $user->id)->first();
        abort_unless($friend?->exists() && $friend->to_user_id == $loginUserId, 404, 'Not Found.');
        $friend->is_seen = true;
        $friend->save();
    }

    public function accept(User $user)
    {
        $loginUser = auth()->user();

        $friend = Friend::getByUsers($loginUser->id, $user->id)->accepted(false)->first();
        abort_unless($friend?->exists() && $friend->to_user_id == $loginUser->id, 404, 'Not Found.');

        try {
            DB::beginTransaction();

            $friend->is_accepted = true;
            $friend->save();

            $this->notificationRepository->add(
                userId: $user->id,
                content: $loginUser->name . ' accepted you friend request.',
                imageUrl: $loginUser->image?->url,
                type: NotificationType::FRIEND->value,
                redirectLink: route('user.show', $loginUser->id)
            );

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

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

            throw new Exception('Friend accepted failed.');
        }
    }

    public function decline(User $user)
    {
        $loginUser = auth()->user();

        $friend = Friend::getByUsers($loginUser->id, $user->id)->accepted(false)->first();
        abort_unless($friend?->exists() && $friend->to_user_id == $loginUser->id, 404, 'Not Found.');

        try {
            DB::beginTransaction();

            $friend->delete();

            $this->notificationRepository->add(
                userId: $user->id,
                content: $loginUser->name . ' declined you friend request.',
                imageUrl: $loginUser->image?->url,
                type: NotificationType::FRIEND->value,
                redirectLink: route('user.show', $loginUser->id)
            );

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

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

            throw new Exception('Friend declined failed.');
        }
    }

    public function cancel(User $user)
    {
        $loginUserId = auth()->id();
        $friend = Friend::getByUsers($loginUserId, $user->id)->accepted(false)->first();
        abort_unless($friend?->exists() && $friend->from_user_id == $loginUserId, 404, 'Not Found.');
        $friend->delete();
    }

    public function remove(User $user)
    {
        $friend = Friend::getByUsers(auth()->id(), $user->id)->accepted(true)->first();
        abort_unless($friend?->exists(), 404, 'Not Found.');
        $friend->delete();
    }
}
