<?php

namespace apexl\Io\modules\user\controllers;

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\exceptions\ValidationException;
use apexl\Io\factories\RequestHelperFactory;
use apexl\Io\includes\Controller;
use apexl\Io\modules\user\Actions\StartLoggedInSession;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\exceptions\InvalidNfcKeyException;
use apexl\Io\modules\user\exceptions\UserAlreadyLoggedInException;
use apexl\Io\modules\user\RequestHelper\LoginByKeyRequestHelper;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\user\services\userTools;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

final readonly class LoginByKeyController extends Controller
{
    public function __invoke(
        RequestHelperFactory $requestHelperFactory,
        StartLoggedInSession $startLoggedInSession,
        userTools $userTools,
        ServerRequestInterface $request,
        ResponseInterface $response
    ): ResponseInterface {

        try {
            $requestHelper = $requestHelperFactory->make(LoginByKeyRequestHelper::class, $request);
        } catch (ValidationException $exception) {
            return $this->json($response, ['error' => $exception->getMessage()], 400);
        }

        try {
            $userId = $request->getAttribute('user'); // set in an earlier middleware
            if ($userId !== 0) {
                throw new UserAlreadyLoggedInException($userId);
            }

            try {
                $user = userEntity::fromNfcKey($requestHelper->nfcKey());
            } catch (RecordNotFoundException) {
                throw new InvalidNfcKeyException();
            }

            if (!$user->active) {
                throw new RecordNotFoundException();
            }

            [$authToken, $refreshToken] = $startLoggedInSession->handle($user);

            $this->output->addResponse(
                $request,
                [
                    'access_token' => $authToken,
                    'refresh_token' => $refreshToken,
                    'expires_in' => currentUser::getTokenExpiryFromNow($authToken),
                ]
            );

            $this->addRedirect($requestHelper->redirect());

            return $this->json($response);
        } catch (UserAlreadyLoggedInException) {
            $this->addRedirect($requestHelper->redirect());

            return $this->json($response);
        } catch (InvalidNfcKeyException) {
            $this->output->addMessage(
                'user.login.validation',
                'error',
                'The provided key is invalid.'
            );

            return $this->json($response, [], 403);
        } catch (RecordNotFoundException) {
            $this->output->addMessage(
                'user.login.validation',
                'error',
                'The user is inactive.'
            );

            return $this->json($response, [], 401);
        }
    }

    private function addRedirect(?string $redirect): void
    {
        $this->output->addRedirect('user.login.redirect', $redirect ?? '/');
    }
}
