<?php

namespace App\Packages\Capsule\Services;

use App\Packages\Capsule\Models\Capsule;
use App\Packages\Capsule\Models\CapsuleTaggedUser;
use App\Packages\Capsule\Models\CapsuleVideo;
use App\Packages\Capsule\Repository\CapsuleTaggedUserRepository;
use App\Packages\Core\Exceptions\RecordExistsException;
use App\Packages\Core\Models\Address;
use App\Packages\Friend\Models\FriendInvite;
use App\Packages\Friend\Models\FriendRelation;
use App\Packages\Friend\Repository\FriendInviteRepository;
use App\Packages\Friend\Repository\FriendRepository;
use App\Packages\User\Manager\UserEntityManager;
use App\Packages\User\Models\User;
use App\Packages\User\Notifications\NotificationTagged;
use App\Packages\User\Notifications\NotificationUploadComplete;
use App\Packages\User\Notifications\TypesNotification;
use App\Packages\User\Repository\UserRepository;
use App\Packages\User\Service\InviteService;
use App\PackagesApi\v3\Requests\Capsule\CompleteRequest;
use App\PackagesApi\v3\Requests\Capsule\TagFriendsRequest;
use App\PackagesApi\v3\Requests\Capsule\UpdateRequest;

/**
 * Class CapsuleCreateService
 * @package App\Packages\Capsule\Services
 */
class CapsuleFactoryService
{
    /**
     * @param User $user
     *
     * @return Capsule
     */
    public function startCreate(User $user)
    {
        return Capsule::createBlankCapsule($user);
    }

    /**
     * @param $fileName
     *
     * @return CapsuleVideo
     */
    public function createVideoRow($fileName)
    {
        $videoData = CapsuleVideo::createFileData();
        $videoData->aws_bucket = env('AWS_VIDEO_BUCKET');
        $videoData->aws_key = $fileName;
        $videoData->save();

        return $videoData;
    }

    /**
     * @param Capsule $capsule
     * @param CompleteRequest $completeRequest
     *
     * @return Capsule
     */
    public function createCapsuleRow(Capsule $capsule, CompleteRequest $completeRequest)
    {
        $capsule->caption = $completeRequest->caption;
        $capsule->is_private = $completeRequest->is_private;
        $capsule->open_at = $completeRequest->opens_at;
        $capsule->is_complete = true;
        $capsule->status = $capsule->determineCapsuleStatus();
        $capsule->save();

        return $capsule;
    }

    public function createCapsuleRowForDemo(Capsule $capsule, $data)
    {
        if (isset($data['caption'])) $capsule->caption = $data['caption'];
        if (isset($data['is_private'])) $capsule->is_private = $data['is_private'];
        if (isset($data['open_at'])) $capsule->open_at = $data['open_at'];
        $capsule->is_complete = true;
        $capsule->status = $capsule->determineCapsuleStatus();
        $capsule->save();

        return $capsule;
    }

    public function updateCapsuleRow(Capsule $capsule, UpdateRequest $updateRequest)
    {
        $capsule->caption = $updateRequest->caption;
        $capsule->is_private = $updateRequest->is_private;
        $capsule->save();

        return $capsule;
    }

    /**
     * @param Capsule $capsule
     * @param CapsuleVideo $capsuleVideo
     *
     * @return Capsule
     */
    public function bindVideoToCapsule(Capsule $capsule, CapsuleVideo $capsuleVideo)
    {
        $capsule->video_id = $capsuleVideo->id;
        $capsule->save();

        return $capsule;
    }

    /**
     * @param Capsule $capsule
     * @param string $placeId
     *
     * @return Capsule
     */
    public function bindAddressToCapsule(Capsule $capsule, string $placeId)
    {
        $address = Address::createFullAddressFromPlace($capsule->user, $placeId);

        $capsule->address_id = $address->id;
        $capsule->save();

        return $capsule;
    }

    /**
     * @param Capsule $capsule
     * @param CompleteRequest $completeRequest
     *
     * @return Capsule
     *
     * @throws \App\Packages\Core\Exceptions\RecordExistsException
     */
    public function completeCreate(Capsule $capsule, CompleteRequest $completeRequest)
    {
        $capsule = $this->createCapsuleRow($capsule, $completeRequest);

        $videoData = $this->createVideoRow($completeRequest->file_name);
        $capsule = $this->bindVideoToCapsule($capsule, $videoData);

        /** @TOOD: After remove location place */
        if ($completeRequest->location_id) {
            $capsule = $this->bindAddressToCapsule($capsule, $completeRequest->location_id);
        }

//        if ($completeRequest->location_place) {
//            $capsule = $this->bindAddressToCapsule($capsule, $completeRequest->location_place);
//        }

        if ($completeRequest->tagged_users) {
            $this->bindTaggedUsersToCapsules($capsule, $completeRequest->tagged_users);
        }

        /** Send notification on capsule upload */
        NotificationUploadComplete::dispatch($capsule, $capsule->user)
            ->onQueue(TypesNotification::QUEUE_NOTIFICATIONS);

        return $capsule;
    }

    public function completeCreateForDemo(Capsule $capsule, $data)
    {
        $capsule = $this->createCapsuleRowForDemo($capsule, $data);

        $videoData = $this->createVideoRow($data['file_name']);
        $capsule = $this->bindVideoToCapsule($capsule, $videoData);

        if (isset($data['location_id'])) {
            $capsule = $this->bindAddressToCapsule($capsule, $data['location_id']);
        }

        if (isset($data['tagged_users'])) {
            $this->bindTaggedUsersToCapsules($capsule, $data['tagged_users']);
        }

        return $capsule;
    }

    /**
     * @param Capsule $capsule
     * @param array $taggedUsers
     *
     * @throws \App\Packages\Core\Exceptions\RecordExistsException
     */
    public function bindTaggedUsersToCapsules(Capsule $capsule, array $taggedUsers)
    {
        /** @var InviteService $inviteService */
        $inviteService = app(InviteService::class);

        foreach ($taggedUsers as $phoneHash) {

            /** Can't tag user owner */
            if ($capsule->user->phone_hash == $phoneHash) {
                continue;
            }

            /** Create tagged user if database does not have one */
            if (!$taggedUser = UserRepository::init()->firstByPhoneHash($phoneHash)) {
                $taggedUser = UserEntityManager::createUserFromPhoneHash($phoneHash);
            }

            /** If user is already tagged in capsule */
            if (CapsuleTaggedUserRepository::init()->existsTaggedUser($capsule, $taggedUser)) {
                continue;
            }

            /** Assign user to capsule and send notification */
            CapsuleTaggedUser::assignTaggedUser($capsule, $capsule->user, $taggedUser);

            /** No send push notificatio if user not accept friend invite */
            if (FriendRepository::init()->existsFriend($taggedUser, $capsule->user)) {
                NotificationTagged::dispatch($capsule, $taggedUser, $capsule->user)
                    ->onQueue(TypesNotification::QUEUE_NOTIFICATIONS);
            }

            $isFriend = FriendRepository::init()->existsFriend($capsule->user, $taggedUser);
            if ($isFriend) {
                continue;
            }

            /** @TODO: Refactore */
            /** If tagged user has already sent invite to user */
            $invite = FriendInviteRepository::init()->getCreatedInvite($taggedUser, $capsule->user);
            if ($invite) {
                $inviteService->acceptInvite($invite);
            } else {
                try {
                    $inviteService->send($capsule->user, $taggedUser);
                } catch (RecordExistsException $exception) {

                }
            }
        }

        $capsule->refreshTaggedUserCount();
    }

    /**
     * @param Capsule $capsule
     * @param TagFriendsRequest $tagFriendsRequest
     *
     * @return Capsule
     *
     * @throws \App\Packages\Core\Exceptions\RecordExistsException
     */
    public function tagFriends(Capsule $capsule, TagFriendsRequest $tagFriendsRequest)
    {
        $taggedUsers = [];
        if ($tagFriendsRequest->tagged_users) {
            $taggedUsers = $tagFriendsRequest->tagged_users;
        }

        /** @TODO: Refactore */
        CapsuleTaggedUserRepository::init()
            ->clearAllExceptTaggedUsersByPhoneHash($capsule, $taggedUsers);

        $this->bindTaggedUsersToCapsules($capsule, $taggedUsers);

        return $capsule;
    }

    /**
     * @param Capsule $capsule
     * @param UpdateRequest $updateRequest
     * @return Capsule
     *
     * @throws RecordExistsException
     */
    public function update(Capsule $capsule, UpdateRequest $updateRequest)
    {
        $capsule = $this->updateCapsuleRow($capsule, $updateRequest);

        /** @TOOD: After remove location place */
        if ($updateRequest->location_id) {
            $capsule = $this->bindAddressToCapsule($capsule, $updateRequest->location_id);
        }

        if ($updateRequest->tagged_users) {
            $taggedUsers = [];
            if ($updateRequest->tagged_users) {
                $taggedUsers = $updateRequest->tagged_users;
            }

            /** @TODO: Refactore */
            CapsuleTaggedUserRepository::init()
                ->clearAllExceptTaggedUsersByPhoneHash($capsule, $taggedUsers);

            $this->bindTaggedUsersToCapsules($capsule, $taggedUsers);
        } else {
            CapsuleTaggedUserRepository::init()
                ->clearAllTaggedUsers($capsule);
        }

        return $capsule;
    }
}
