<?php

namespace apexl\Io\modules\user\controllers;

use apexl\Io\exceptions\RecordNotFoundException;
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\Request\LoginByKeyRequest;
use apexl\Io\modules\user\services\TokenExpiryCalculator;
use JsonException;
use Psr\Http\Message\ResponseInterface;

final readonly class LoginByKeyController extends Controller
{
    /**
     * @throws JsonException
     */
    public function __invoke(
        StartLoggedInSession $startLoggedInSession,
        LoginByKeyRequest $request,
        ResponseInterface $response,
        TokenExpiryCalculator $expiryCalculator,
    ): ResponseInterface {
        try {
            $userId = $request->getAttribute('user'); // set in an earlier middleware
            if ($userId !== 0) {
                throw new UserAlreadyLoggedInException($userId);
            }

            try {
                $user = userEntity::fromNfcKey($request->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' => $expiryCalculator->fromNow($authToken),
                ]
            );

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

            return $this->json($response);
        } catch (UserAlreadyLoggedInException) {
            $this->addRedirect($request->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 ?? '/');
    }
}
