<?php


namespace App\Model\Facades\Readers;


use App\Model\Entities\Security\BattleNetAccount;
use App\Model\Entities\Security\SteamAccount;
use App\Model\Entities\Security\User;
use App\Model\Entities\Security\UserCredentials;
use App\Model\Exceptions\BattleNet\UnknownBattleNetAccountIdException;
use App\Model\Exceptions\Steam\AuthenticationFailedException;
use App\Model\Exceptions\Steam\AuthenticationRequiredException;
use App\Model\Exceptions\Steam\UnknownSteamIdException;
use App\Model\Services\BattleNetApiService;
use App\Model\Services\BattleNetService;
use App\Model\Services\SteamApiService;
use App\Model\Services\SteamService;
use App\Model\Services\UserCredentialsService;
use Doctrine\ORM\EntityManager;
use Throwable;

class UserAccountReader extends BaseReader
{
    /** @var EntityManager */
    protected $entityManager;

    /** @var UserCredentialsService */
    protected $userCredentialsService;
    /** @var SteamService */
    protected $steamService;
    /** @var BattleNetService */
    protected $battleNetService;
    /** @var SteamApiService */
    protected $steamApiService;
    /** @var UserReader */
    protected $userReader;
    /** @var BattleNetApiService */
    protected $battleNetApiService;

    public function __construct(UserReader $userReader,
                                UserCredentialsService $userCredentialsService,
                                SteamService $steamService,
                                SteamApiService $steamApiService,
                                BattleNetService $battleNetService,
                                BattleNetApiService $battleNetApiService)
    {


        $this->userCredentialsService = $userCredentialsService;
        $this->steamService = $steamService;
        $this->battleNetService = $battleNetService;
        $this->steamApiService = $steamApiService;
        $this->userReader = $userReader;
        $this->battleNetApiService = $battleNetApiService;
    }

    /**
     * Battle.net auth requires entity manager to save new access token
     * DO NOT USE IT ANYWHERE ELSE!!!
     * @param EntityManager $entityManager
     */
    public function injectEntityManager(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * @param $backlink
     * @return User
     * @throws AuthenticationFailedException
     * @throws AuthenticationRequiredException
     * @throws UnknownSteamIdException
     */
    public function steamAuth($backlink) : User
    {
        $steamId = $this->steamApiService->retrieveSteamId($backlink);
        $steamAccount = $this->steamService->findSteamAccountBySteamId($steamId);

        if ($steamAccount === null) {
            throw new UnknownSteamIdException();
        }

        return $steamAccount->getUser();
    }

    public function getSteamAuthRedirectUrl($backlink)
    {
        return $this->steamApiService->getAuthRedirectUrl($backlink);
    }

    public function battleNetAuth($backlink, $region = 'eu')
    {
        $code = $this->battleNetApiService->retrieveAuthorizationCode();
        $accessToken = $this->battleNetApiService->retrieveAccessToken($backlink, $region, $code);
        $data = $this->battleNetApiService->retrieveUserAccount($backlink, $region, $accessToken);

        $battleNetAccount = $this->battleNetService->findBattleNetAccountByBattleNetId($data->getProfileId());

        if ($battleNetAccount === null) {
            throw new UnknownBattleNetAccountIdException();
        }

        try {
            $this->entityManager->beginTransaction();
            $battleNetAccount->setAccessToken($accessToken->getToken());
            $this->entityManager->commit();
        } catch (Throwable $e) {
            $this->entityManager->rollback();
            throw $e;
        }

        return $battleNetAccount->getUser();
    }

    public function getBattleNetAuthorizationUrl($backlink, $region = 'eu')
    {
        return $this->battleNetApiService->getAuthorizationUrl($backlink, $region);
    }

    public function hasGuildGateAccount($user) : bool
    {
        $userEntity = $this->userReader->validateUser($user);
        $account = $this->userCredentialsService->getUserCredentials($userEntity);

        return ($account instanceof UserCredentials);
    }

    public function hasSteamAccount($user) : bool
    {
        $userEntity = $this->userReader->validateUser($user);
        $account = $this->steamService->getUsersSteamAccount($userEntity);

        return ($account instanceof SteamAccount);
    }

    public function hasBattleNetAccount($user) : bool
    {
        $userEntity = $this->userReader->validateUser($user);
        $account = $this->battleNetService->getUsersBattleNetAccount($userEntity);

        return ($account instanceof BattleNetAccount);
    }

    public function findCredentialsByEmail($email) : UserCredentials
    {
        $account = $this->userCredentialsService->findCredentialsByEmail($email);
        $this->validateEntity($account);

        return $account;
    }
}