<?php

namespace App\PackagesApi\v4\Controllers\User;

use App\Http\Controllers\Controller;
use App\Packages\Core\Exceptions\RecordNotExistsException;
use App\Packages\v4\Phone\Service\VerificationCodeService;
use App\Packages\v4\User\Jobs\SendVerifyEmailJob;
use App\Packages\v4\User\Manager\DemoUserManager;
use App\Packages\v4\User\Manager\TokenManager;
use App\Packages\v4\User\Manager\UserEntityManager;
use App\Packages\User\Models\User;
use App\Packages\User\Models\UserEmailToken;
use App\Packages\User\Models\UserPhoneConfirm;
use App\Packages\v4\User\Repository\UserEmailTokenRepository;
use App\Packages\v4\User\Repository\UserRepository;
use App\Packages\v4\User\Service\UserSocialService;
use App\PackagesApi\v4\Requests\Auth\EmailLoginRequest;
use App\PackagesApi\v4\Requests\Auth\EmailRegisterCompleteRequest;
use App\PackagesApi\v4\Requests\Auth\EmailRegisterInitRequest;
use App\PackagesApi\v4\Requests\Auth\EmailRegisterValidateRequest;
use App\PackagesApi\v4\Requests\Auth\PhoneCodeRequest;
use App\PackagesApi\v4\Requests\Auth\PhoneRequest;
use App\PackagesApi\v4\Requests\Auth\SocialLoginRequest;
use App\PackagesApi\Status\StatusCode;
use Illuminate\Http\JsonResponse;
use Lcobucci\JWT\Token;
use Throwable;

/**
 * Class AuthController
 *
 * @package App\PackagesApi\Controllers\v4
 */
class AuthController extends Controller
{
    /** @var VerificationCodeService */
    private $verificationCodeService;

    public function __construct(VerificationCodeService $verificationCodeService)
    {
        $this->verificationCodeService = $verificationCodeService;
    }

    /**
     * @param PhoneRequest $request
     *
     * @return JsonResponse
     */
    public function sendPhoneCode(PhoneRequest $request)
    {
        $return = [
            'exists'       => false,
            'has_nickname' => false
        ];

        $user = UserRepository::init()
            ->firstRegisteredByPhoneNumber($request->phone_code, $request->phone_number);

        if ($user) {
            $return['exists'] = true;
            $return['has_nickname'] = ($user->nickname) ? true : false;

            if ($user->nickname) {
                $return['has_nickname'] = validateNickname($user->nickname) ? true : false;
            }
        }

        /** @TODO: Refactore test case */
        if ($request->phone_code == '371' && $request->phone_number == '00000000') {
            UserPhoneConfirm::createTestPhoneConfirm($request->phone_code, $request->phone_number);

            return jsonSuccess($return);
        }

        $this->verificationCodeService
            ->startVerify($request->phone_code, $request->phone_number);

        return jsonSuccess($return);
    }

    /**
     * @param PhoneCodeRequest $phoneCodeRequest
     * @param TokenManager $tokenManager
     *
     * @return JsonResponse
     */
    public function phoneLogin(PhoneCodeRequest $phoneCodeRequest, TokenManager $tokenManager)
    {
        try {
            $phoneConfirm = $this->verificationCodeService
                ->completeVerify($phoneCodeRequest->code);

        } catch (RecordNotExistsException $e) {
            return jsonErrorMessage($e->getMessage());
        }

        $phoneHash = phoneHash($phoneConfirm->phone_code, $phoneConfirm->phone_number);
        $user = UserRepository::init()->firstByPhoneHash($phoneHash);

        $new_user = false;
        $has_username = false;

        if (!$user) {
            $user = UserEntityManager::createUser(
                $phoneConfirm->phone_code,
                $phoneConfirm->phone_number
            );

            $new_user = true;

            //DemoUserManager::makeFriend($user);
            //DemoUserManager::createPrivateCapsule($user);
        }

        if (!$user->is_registered) {
            $user->makeRegistered($phoneConfirm->phone_code, $phoneConfirm->phone_number);

            $new_user = true;

            //DemoUserManager::makeFriend($user);
            //DemoUserManager::createPrivateCapsule($user);
        }

        if ($user->nickname) {
            $has_username = validateNickname($user->nickname) ? true : false;
        }

        $token = $tokenManager->generateToken($user);

        return jsonSuccess([
            'onboarding_done' => (bool) $user->is_on_boarding_done,
            'token'           => $token->access_token,
            'is_new'          => $new_user,
            'has_nickname'    => $has_username,
            'has_email'       => (bool) $user->email
        ]);
    }

    /**
     * @param EmailRegisterInitRequest $request
     * @return JsonResponse
     * @throws \Exception
     */
    public function emailRegisterInit(EmailRegisterInitRequest $request)
    {
        $checkEmail = UserRepository::init()->firstByEmailWithPassword($request->email);

        if ( $checkEmail ) {
            return jsonError([
                'message' => 'Email already exist',
            ], 409);
        }

        $emailHash = UserEmailToken::createToken($request->email);

        /** Send email */
        SendVerifyEmailJob::dispatchNow($emailHash);

        return jsonSuccess([
            'message' => 'ok'
        ]);
    }

    /**
     * @param EmailRegisterValidateRequest $request
     * @return JsonResponse
     */
    public function emailRegisterValidate(EmailRegisterValidateRequest $request)
    {
        $checkHash = UserEmailTokenRepository::init()->findByToken($request->token);

        if ( ! $checkHash ) {
            return jsonError([
                'message' => 'This email verify not exist',
            ], 422);
        }

        return jsonSuccess([
            'message' => 'ok'
        ]);
    }

    /**
     * @param EmailRegisterCompleteRequest $request
     * @param TokenManager $tokenManager
     * @return JsonResponse
     * @throws \Exception
     */
    public function emailRegisterComplete(EmailRegisterCompleteRequest $request, TokenManager $tokenManager)
    {
        $checkHash = UserEmailTokenRepository::init()->findByToken($request->token);

        if ( ! $checkHash ) {
            return jsonError([
                'message' => 'This token not exist'
            ], 422);
        }

        $user = UserEntityManager::createUserFromEmail($checkHash->email, $request->nickname, $request->password);

        //DemoUserManager::makeFriend($user);
        //DemoUserManager::createPrivateCapsule($user);

        $token = $tokenManager->generateToken($user);

        $checkHash->delete();

        $has_nickname = false;
        if ($user->nickname) {
            $has_nickname = validateNickname($user->nickname) ? true : false;
        }

        return jsonSuccess([
            'onboarding_done' => (bool) $user->is_on_boarding_done,
            'token' => $token->access_token,
            'has_nickname' => $has_nickname,
            'has_email' => (bool) $user->email
        ]);
    }

    /**
     * @param EmailLoginRequest $request
     * @param TokenManager $tokenManager
     * @return JsonResponse
     */
    public function emailLogin(EmailLoginRequest $request, TokenManager $tokenManager)
    {
        $user = UserRepository::init()->firstByEmailWithPassword($request->email);

        if ( ! $user ) {
            return jsonError([
                'message' => 'Email not registered',
            ], 422);
        }

        if ( ! \Hash::check($request->password, $user->password) ) {
            return jsonError([
                'message' => 'Password not valid'
            ], 422);
        }

        $token = $tokenManager->generateToken($user);

        $has_nickname = false;
        if ($user->nickname) {
            $has_nickname = validateNickname($user->nickname) ? true : false;
        }

        return jsonSuccess([
            'onboarding_done' => (bool) $user->is_on_boarding_done,
            'token'           => $token->access_token,
            'has_nickname'    => $has_nickname,
            'has_email'       => (bool) $user->email
        ]);

    }

    /**
     * @param SocialLoginRequest $request
     * @param TokenManager $tokenManager
     * @return null
     * @throws Throwable
     */
    public function facebookLogin(SocialLoginRequest $request, TokenManager $tokenManager)
    {
        $socialResponse = UserSocialService::getSocialUserByToken('facebook', $request->token);

        if (!$socialResponse)
            return jsonError(['message' => 'Failed to get information.'], 422);

        $user = UserRepository::init()->firstByFacebookResponse($socialResponse);

        $is_new = false;

        if (!$user) {
            if ( ! $socialResponse->getEmail() )
                return jsonError([
                    'message' => 'Email not provided'
                ], StatusCode::HTTP_CONFLICT);

            $user = UserEntityManager::createUserFromFacebook($socialResponse);

            $is_new = true;

            //DemoUserManager::makeFriend($user);
            //DemoUserManager::createPrivateCapsule($user);
        }

        $token = $tokenManager->generateToken($user);

        $has_nickname = false;
        if ($user->nickname) {
            $has_nickname = validateNickname($user->nickname) ? true : false;
        }

        return jsonSuccess([
            'onboarding_done' => (bool) $user->is_on_boarding_done,
            'token'           => $token->access_token,
            'is_new'          => $is_new,
            'has_nickname'    => $has_nickname,
            'has_email'       => (bool) $user->email
        ]);
    }

    /**
     * @param SocialLoginRequest $request
     * @param TokenManager $tokenManager
     * @return JsonResponse
     * @throws Throwable
     */
    public function googleLogin(SocialLoginRequest $request, TokenManager $tokenManager)
    {
        $socialResponse = UserSocialService::getSocialUserByGoogleIdToken($request->token);

        if (!$socialResponse)
            return jsonError(['message' => 'Failed to get information.'], 422);

        $user = UserRepository::init()->firstByGoogleResponse($socialResponse);

        $is_new = false;

        if (!$user) {
            if ( ! $socialResponse->getEmail() )
                return jsonError([
                    'message' => 'Email not provided'
                ], StatusCode::HTTP_CONFLICT);

            $user = UserEntityManager::createUserFromGoogle($socialResponse);

            $is_new = true;

            //DemoUserManager::makeFriend($user);
            //DemoUserManager::createPrivateCapsule($user);
        }

        $token = $tokenManager->generateToken($user);

        $has_nickname = false;
        if ($user->nickname) {
            $has_nickname = validateNickname($user->nickname) ? true : false;
        }

        return jsonSuccess([
            'onboarding_done' => (bool) $user->is_on_boarding_done,
            'token'           => $token->access_token,
            'is_new'          => $is_new,
            'has_nickname'    => $has_nickname,
            'has_email'       => (bool) $user->email
        ]);
    }

    public function appleLogin(SocialLoginRequest $request, TokenManager $tokenManager)
    {
        //$socialResponse = UserSocialService::getSocialUserByAppleIdToken($request->token);
        $socialResponse = UserSocialService::getSocialUserByToken('sign-in-with-apple', $request->token);

        if (!$socialResponse)
            return jsonError(['message' => 'Failed to get information.'], 422);

        $user = UserRepository::init()->firstByAppleResponse($socialResponse);

        $is_new = false;

        if (!$user) {
            if ( ! $socialResponse->getEmail() )
                return jsonError([
                    'message' => 'Email not provided'
                ], StatusCode::HTTP_CONFLICT);

            $user = UserEntityManager::createUserFromApple($socialResponse);

            $is_new = true;

            //DemoUserManager::makeFriend($user);
            //DemoUserManager::createPrivateCapsule($user);
        }

        $token = $tokenManager->generateToken($user);

        $has_nickname = false;
        if ($user->nickname) {
            $has_nickname = validateNickname($user->nickname) ? true : false;
        }

        return jsonSuccess([
            'onboarding_done' => (bool) $user->is_on_boarding_done,
            'token' => $token->access_token,
            'is_new' => $is_new,
            'has_nickname' => $has_nickname,
            'has_email' => (bool) $user->email
        ]);
    }
}
