<?php

namespace apexl\Io\modules\user\controllers;

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\includes\Controller;
use apexl\Io\modules\user\entities\refreshTokenEntity;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\exceptions\ClientCredentialsValidationException;
use apexl\Io\modules\user\exceptions\RefreshTokenExpiredException;
use apexl\Io\modules\user\exceptions\RefreshTokenUserMismatchException;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\user\services\RefreshTokenSessionIdFinder;
use Exception;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

final readonly class RefreshController extends Controller
{
    public function __invoke(
        RefreshTokenSessionIdFinder $sessionIdFinder,
        ServerRequestInterface $request,
        ResponseInterface $response,
    ): ResponseInterface {
        $body = (object) $request->getParsedBody();

        try {
            if (isset($body->refresh_token)) {
                $refreshTokenEntity = new refreshTokenEntity();
                $refreshTokenEntity->loadByToken(hash('sha256', (string) $body->refresh_token));
                if ($refreshTokenEntity->isExpired()) {
                    throw new RefreshTokenExpiredException('Token expired');
                }

                if ($sessionId = $sessionIdFinder->find($request, $refreshTokenEntity)) {
                    // Refresh token still valid?
                    $user = new userEntity();
                    $user->load($refreshTokenEntity->user_id);
                    $authToken = currentUser::createJWT(
                        $user,
                        $sessionId,
                        config('auth.jwt.secret_key'),
                        config('auth.jwt.algorithm'),
                        config('auth.jwt.lifetime')
                    );
                    $refreshTokenEntity->expiry = refreshTokenEntity::newExpiryFrom();
                    $refreshTokenEntity->store();

                    $this->output->addResponse($request, ['access_token' => $authToken]);

                    return $this->json($response);
                }
            }
        } catch (
        ClientCredentialsValidationException|
        RefreshTokenUserMismatchException|
        RefreshTokenExpiredException
        $exception
        ) {
            return $this->json(
                $response,
                ['error' => $exception->getMessage()],
                401,
            );
        } catch (RecordNotFoundException $exception) {
            return $this->json(
                $response,
                ['error' => $exception->getMessage()],
                404,
            );
        } catch (Exception $exception) {
            return $this->json(
                $response,
                ['error' => $exception->getMessage()],
                500,
            );
        }

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

}
