<?php

namespace apexl\Io\modules\user\controllers;

use apexl\Io\includes\Controller;
use apexl\Io\modules\user\Actions\StartLoggedInSession;
use apexl\Io\modules\user\Actions\ValidateLoginAction;
use apexl\Io\modules\user\exceptions\LoginException;
use apexl\Io\modules\user\exceptions\UserAlreadyLoggedInException;
use apexl\Io\modules\user\Request\LoginRequest;
use apexl\Io\modules\user\services\TokenExpiryCalculator;
use Exception;
use Psr\Http\Message\ResponseInterface;

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

        } catch (UserAlreadyLoggedInException) {
            $this->addRedirect($request->redirect());

            return $this->json($response);
        }

        try {
            $user = $loginAction->handle($request->username(), $request->password());

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

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

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

            return $this->json($response);
        } catch (LoginException $exception) {
            $this->output->addMessage('user.login.validation', 'error', $exception->getMessage());
            $this->output->addResponse($request, [], false); // added so we can hook into this elsewhere.

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

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

}
